最小生成树——克鲁斯克算法+一道例题
Posted rainyroad
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最小生成树——克鲁斯克算法+一道例题相关的知识,希望对你有一定的参考价值。
//最小生成树,Kruskal算法 struct rec { int x; int y; int z; }edge[50010]; int fa[10010],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++) { int x=get(edge[i].x); int y=get(edge[i].y); if(x==y) continue; fa[x]=y; ans+=edhe[i].z; } cout<<ans<<endl; } //给定一颗N个节点的树,要求增加若干条边,把这棵树扩充成为完全图 //并满足图的唯一最小生成树仍然是这棵树,求增加的边的权值总和最小是多少 //一开始我的错误思想是先把一颗生成树给它生成出来 //然后在去找到那条权值最大的边(max),然后最后在计算出没有相互连接的结点对数(num) //最后用(num)*max,就计算出权值总和最小是多少了。 //但是这样做的话相当于没有用到最小生成树的算法 //所以正确的做法应该是,在生成最小生成树的过程当中就把权值总和最小计算出来了, //这样就使用到了最小生成树的算法,我们需要在模板代码的基础上改动一些地方 //加一个S数组记录每个森林(即并查集)中的结点个数,这样不同的森林连接时才知道 //有多少个点对需要相互连接,并且连接的权值应当时了两个森林连接时的最小边的权值+1 //从每个森林只有一个结点,扩展到只剩下一个森林,即一颗最小生成树生成时,则答案就刚好算出来了 struct rec{ int x; int y; int z; }edge[maxn]; int fa[6010],s[6010],n,T; typedef long long l; l 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>>T; while(T--) { cin>>n; for(int i=1;i<n;i++) { scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z); } for(int i=1;i<=n;i++) { fa[i]=i; s[i]=1; //初始化,相当于生成n个森林,每个森林只有一个结点 //一个结点一颗树。 } sort(edge+1,edge+n); for(int i=1;i<n;i++) { x=get(edge[i].x); y=get(edge[i].y); if(x==y) continue; fa[x]=y; s[y]+=s[x];//x以y作为父节点,做父节点y就应该时这个森林的代表节点 ans+=(l)(edge[i].z+1)*(s[x]*s[y]-1); //连接成最小生成树的同时,把两个并查集当中不相互连接的点全部虚拟连接了一个权值为edge[i].z+1的边 } cout<<ans<<endl; } }
以上是关于最小生成树——克鲁斯克算法+一道例题的主要内容,如果未能解决你的问题,请参考以下文章