kruskal's algorithm.[克鲁斯卡尔算法]

克鲁斯卡尔算法
假设 WN=(V,{E}) 是一个含有 n 个顶点的连通网,则按照克鲁斯卡尔算法构造 最小生成树的过程为:先构造一个只含 n 个顶点,而边集为空的子图,若将该子图中各个顶点看成是各棵树上的根结点,则它是一个含有 n 棵树的一个森林。之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直至森林中只有一棵树, 也即子图中含有 n-1条边为止。
/本程序用到了并查集的基本操作,不会并查集的请自行学习或参考本代码学习
//getfa为查询祖先,merge为将集合合并,same是判断两个点是否处于同一集合
//getfa操作中使用了路径压缩即return fa[x] = getfa(fa[x]),这样可以减小并查集森林退化所带来的时间复杂度
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN_E 100000
#define MAXN_V 100000
using namespace std;
struct Edge{
    int fm,to,dist;
}e[MAXN_E];
int fa[MAXN_V],n,m;
bool cmp(Edge a,Edge b){
    return a.dist < b.dist;
}
int getfa(intx){//getfa是在并查集森林中找到x的祖先
    if(fa[x]==x) return fa[x];
    else return fa[x] = getfa(fa[x]);
}
int same(int x,int y){
    return getfa(x)==getfa(y);
}
void merge(intx,inty){
    int fax=getfa(x),fay=getfa(y);
    fa[fax]=fay;
}
int main(){
    scanf("%d%d",&n,&m);//n为点数,m为边数
    for(inti=1;i<=m;i++)
    scanf("%d%d%d",&e[i].fm,&e[i].to,&e[i].dist);//用边集数组存放边,方便排序和调用
    sort(e+1,e+m+1,cmp);//对边按边权进行升序排序
    for(int i=1;i<=n;i++)
    fa[i]=i;
    int rst=n,ans=0;//rst表示目前的点共存在于多少个集合中,初始情况是每个点都在不同的集合中
    for(int i=1;i<=m && rst>1;i++)
    {
        int x=e[i].fm,y=e[i].to;
        if(same(x,y)) continue;//same函数是查询两个点是否在同一集合中
        else
        {
            merge(x,y);//merge函数用来将两个点合并到同一集合中
            rst--;//每次将两个不同集合中的点合并,都将使rst值减1
            ans+=e[i].dist;//这条边是最小生成树中的边,将答案加上边权
        }
    }
    printf("%d\n",ans);
    return0;
}


你可能感兴趣的:(算法,图论,kruskal)