NOIP模板复习(1) 并查集与Kruskal算法
目录
1.并查集
并查集是一种可以高效查询和合并的树型数据结构。
1.1结构原理
并查集是通过记录并查询节点的父亲节点来实现判断两节点是否有联系的数据结构,可以实现对n个节点实行(O(alpha(n)))级别的查询和合并操作((alpha(n))为阿克曼(Ackermann)函数的反函数,其增长十分缓慢,基本可以视为常数。)
1.2结构实现
代码如下:
int father[i]; //每个节点的父亲
//初始化n个节点
void pre(int n)
{
for(int i=1;i<=n;i++)
{
father[i]=i;
}
return ;
}
//查找当前元素所在树的根节点(代表元素)
int find(int x)
{
if(x==father[x])
{
return x;
}
return father[x]=find(father[x]);//在查找的同时时,将节点直连到根节点
}
//合并元素x,y所处的集合,直接合并两集合的根节点即可
void unions(int x, int y)
{
//查找到x,y的根节点
x=find(x);
y=find(y);
if(x==y)
{
return ;
}
father[x]=y;
return ;
}
//判断x,y是属于同一个集合
bool check(int x,int y)
{
return find(x)==find(y);
}
2.Kruskal算法:
Kruskal算法是一种在无向图中求最小生成树的算法,所谓最小生成树就是指所有点都在同一个连通分量中且边权和最小的连通图。而Kruskal算法就是基于贪心的求最小生成树的算法。
2.1算法原理:
上面的介绍也提到了该算法是基于贪心原理。具体操作是每次从原图中取出一条最短的边再判断边所连的两个点是否在一个连通分量中。该算法的正确性其实很好理解,首先可以想到的是最小生成树两点间的边一定是最短的(可以通过反面去思考)。所以每次取最短边是正确的,而当取出的边已经使图连通了,所生成的图就一定是最小生成树。
2.2算法流程:
1.除原图外再建立一个新图(G_{new})使新图具有原图全部的节点,但没有一条边。
2.将原图中的所有边按边权从小到大排序,记为边集(E_{min})。
3.从(E_{min})中取出边权最小的边,判断该边所相连的两个顶点是否在一个连通分量中,如果不在一个连通分量中则执行第4步,否则重复第3步直到边取完。
4.将取出的边加入(G_{new})中,判断是否所有的点都在同一连通分量中,如果是算法结束否则返回第3步。
2.3算法实现
下面提供伪代码和HDU1863畅通工程代码一份。
伪代码:
Kruskal()
{
SORT(Edge)
依次取出最小边
{
if:(Edge两点不处于同一集合)
{
合并Edge连接两点
边数++
}
}
}
HDU1863 畅通工程(Kruskal板子题):
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <algorithm>
using namespace std;
struct point
{
int u,v,w;
bool operator <(const point& mid)const
{
return this->w<mid.w;
}
};
point edge[10000];
int father[110];
void pre(int n)
{
for(int i=1;i<=n;i++)
{
father[i]=i;
}
return ;
}
int find(int x)
{
if(x==father[x])
{
return x;
}
return father[x]=find(father[x]);
}
void unions(int x, int y)
{
x=find(x);
y=find(y);
if(x==y)
{
return ;
}
father[x]=y;
return ;
}
int Kruskal(int n)
{
int ans=0;
for(int i=1;i<=n;i++)
{
int x=find(edge[i].u);
int y=find(edge[i].v);
if(x!=y)
{
ans+=edge[i].w;
unions(x,y);
}
}
return ans;
}
int main()
{
int n,m;
while(scanf("%d %d",&n,&m),n)
{
pre(m);
for(int i=1;i<=n;i++)
{
scanf("%d %d %d",&edge[i].u,&edge[i].v,&edge[i].w);
}
sort(edge+1,edge+n+1);
int ans=Kruskal(n);
int num=0;
for(int i=1;i<=m;i++)
{
if(father[i]==i)
{
num++;
}
}
if(num==1)
{
cout<<ans<<endl;
}
else
{
cout<<"?"<<endl;
}
}
return 0;
}