定理:任何一棵最小生成树一定包含无向图中权值最小的边(反证法)
算法思路
Kruskal算法基于上述定理,总是维护无向图的最小生成森林。最初,可认为生成森林由0条边构成,每个节点各自为树。
在任意时刻,从剩余边中选出一条权值最小的,并且两个端点不连通(属于两棵不同的树),把该边加入生成森林。
节点的连通情况用并查集维护。
算法流程
1.建立并查集,每个点各自为一个集合
2.把所有边按权值升序排列,依次扫描每条边(x,y,z)
3.若x,y联通,则忽略这条边,继续扫描下一条
4.否则,合并x,y所在集合,把z累加到答案中
5.所有边完成扫描后,第4步中处理过的边就构成最小生成树
程序O(mlogm)
//by ziwan Catherine
//Kruskal算法
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct rec{
int x,y,z;
}edge[500010];
int fa[100010],n,m,ans;
bool operator <(rec a,rec b){
return a.z<b.z;
}
int get(int x){
if(x==fa[x]) return x;
return fa[x]=get(fa[x]);
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++)
scanf("%d%d%d",edge[i].x,edge[i].y,edge[i].z);
sort(edge+1,edge+m+1);//把所有边按权值升序排列
for(int i=1;i<=n;i++) fa[i]=i;//每个点各自为一个集合
for(int i=1;i<=m;i++){//依次扫描每条边(x,y,z)
int x=get(edge[i].x);
int y=get(edge[i].y);
if(x==y) continue;//若x,y联通,则忽略这条边,继续扫描下一条
fa[x]=y;
ans+=edge[i].z;//否则,合并x,y所在集合,把z累加到答案中sa
}
cout<<ans<<endl;
return 0;
}