【简介】克鲁斯卡尔算法是一种用来寻找最小生成树的算法。在剩下的所有未选取的边中,找最小边,如果和已选取的边构成回路,则放弃,选取次小边。即给定一些线段的x,y和权值,当x,y不在一个集合的时候,选取权值最小的一个边,然后把x,y合并到一个集合中(并查集维护)
我们用现在来模拟一下Kruskal算法,下面给出一个无向图B,我们使用Kruskal来找无向图B的最小生成树。
首先,我们将所有的边都进行从小到大的排序。排序之后根据贪心准则,我们选取最小边(A,D)。我们发现顶点A,D不在一棵树上,所以合并顶点A,D所在的树,并将边(A,D)加入边集E。
我们接着在剩下的边中查找权值最小的边,于是我们找到的(C,E)。我们可以发现,顶点C,E仍然不在一棵树上,所以我们合并顶点C,E所在的树,并将边(C,E)加入边集E
不断重复上述的过程,于是我们就找到了无向图B的最小生成树,如下图所示!
核心代码:
初始化并查集
for (int i=1;i<=n;i++) fat[i]=i;//初始化并查集
并查集中的查询和合并操作
int ask(int x){//查找是否在同一集合
if (fat[x]!=x) fat[x]=ask(fat[x]);
return fat[x];
}
void unite (int x,int y){
int x1=ask(x),x2=ask(y);
if (x1==x2) return;
else fat[x1]=x2;
}
另外,使用结构体存储边的x,y坐标和权值。
模板题:P3366 【模板】最小生成树
代码如下:
#include<cstdio>
#include<algorithm>
#define N (5010)
#define M (200010)
using namespace std;
//定义结构体存边,x为起点,y为终点,z为值
struct edge{
int x,y,z;
}a[M];
int fat[N],tot,ans;
bool cmp(edge x,edge y){//用于sort的排序,按值排
return x.z<y.z;
}
int ask(int x){//查找是否在同一集合
if (fat[x]!=x) fat[x]=ask(fat[x]);
return fat[x];
}
void unite (int x,int y){
int x1=ask(x),x2=ask(y);
if (x1==x2) return;
else fat[x1]=x2;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
sort(a+1,a+m+1,cmp);//把边按从大到小排
for (int i=1;i<=n;i++) fat[i]=i;//初始化并查集
for (int i=1;i<=m;i++){
if (ask(a[i].x)!=ask(a[i].y)){//克鲁斯卡尔
tot++;
ans=ans+a[i].z;
unite(a[i].x,a[i].y);
}
}
if (tot<n-1) printf("orz");//判断若tot=k-1则生成了最小树
else printf("%d",ans);
return 0;
}
注意,判断若tot=k-1则生成了最小树。