标签:pre 连通 int Luogu Kruskal 最小 生成 权值
Kruskal(克鲁斯卡尔)算法:
“求加权连通图的最小生成树的算法。” ——百度百科 我们直接来看Kruskal算法的实现思想:对于每一条边,按权值从小到大排序,然后遍历。判断一条边的两点是否已经连通,若未连通,则把这条边加入图中,否则继续判断下一条边,直到构建出最小生成树。(一种贪心算法的应用) 看完上面,不知道你理解了吗?不管是否明白,我们下面结合一个例子再看一遍Kruskal是如何实现的。 对于下面的图,它有6个点和9条边,每条边上有相应的权值,不过目前这些边都还没有加入到图中。 首先我们从权值为1的边看起(按权值从小到大),很显然,它的两个点还没有连通,我们把它加入图中。 同样,我们接着把权值为2、3的两条边加入到图中。 然而在判断权值为4的边的时候,要小心,它的两端点已经连通(c→b→d),所以我们跳过这条边。 同理,我们接下来跳过权值为5、7、9的边,且将权值为6、8的边加入到图中,得到了我们的最小生成树。 相信通过上面的例子,你对Kruskal已经有了更深的理解,现在我们来思考如何用代码实现我们在上面模拟的过程。
首先我们先要解决两个问题: ①如何表示这些边、点及边的权值。 ②如何判断两个点是否连通。 要解决这两个问题,我们需要两个工具: ·一个结构体,用来储存边的信息:
1 struct edge 2 { 3 int start,to,value; //起点、终点和权值,value的值可以不是整型 4 bool operator <(edge a)const //重载运算符,作用相当于一个cmp函数 5 { 6 return value<a.value; 7 } 8 }e[num_edge];
·利用并查集,来判断点是否连通:
(如还未学习并查集,可点此跳转学习(施工中orz))1 int union_find(int x) 2 { 3 int r=x; 4 while(pre[r]!=r) //寻找祖先。这样的话,我们就需要一个pre数组记录这个点的祖先 5 { 6 r=pre[r]; 7 } 8 int i=x,j; 9 while(i!=r) //路径压缩,可不加 10 { 11 j=pre[i]; 12 pre[i]=r; 13 i=j; 14 } 15 return r; 16 }
有了这两个工具,我们就可以用代码来实现Kruskal了:
void kruskal() { int i; total=0; //记录已经加入图中的边的数量 val=0; //记录权值之和 for(i=0;i<m;i++) //遍历边 { int u,v; u=union_find(e[i].start); v=union_find(e[i].to); if(u==v) continue; //判断这个边的两点是否连通,如连通,则跳过 total++; pre[u]=v; //将两点更新为连通 val+=e[i].value; if(total==n-1) break; //若total=n-1则已经生成最小生成树,可跳出循环了 } return ; }
代码:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 int pre[5005],n,m,val,total; 6 7 struct edge 8 { 9 int start,to,value; 10 bool operator <(edge a)const 11 { 12 return value<a.value; 13 } 14 }e[200005]; 15 16 int union_find(int x) 17 { 18 int r=x; 19 while(pre[r]!=r) 20 { 21 r=pre[r]; 22 } 23 int i=x,j; 24 while(i!=r) 25 { 26 j=pre[i]; 27 pre[i]=r; 28 i=j; 29 } 30 return r; 31 } 32 33 void kruskal() 34 { 35 int i; 36 total=0; 37 val=0; 38 for(i=0;i<m;i++) 39 { 40 //cout<<"test"<<' '; 41 int u,v; 42 u=union_find(e[i].start); 43 v=union_find(e[i].to); 44 // cout<<u<<' '<<v<<'*'<<endl; 45 if(u==v) continue; 46 total++; 47 pre[u]=v; 48 val+=e[i].value; 49 if(total==n-1) break; 50 } 51 } 52 53 int main() 54 { 55 int i; 56 scanf("%d%d",&n,&m); 57 for(i=0;i<n;i++) pre[i]=i; 58 for(i=0;i<m;i++) scanf("%d%d%d",&e[i].start,&e[i].to,&e[i].value); 59 sort(e,e+m); 60 kruskal(); 61 //cout<<total<<'*'<<endl; 62 if(total==n-1) printf("%d",val); 63 else printf("orz"); 64 return 0; 65 }Luogu P3366
其他题目:
Luogu P2330 [SCOI2005]繁忙的都市(难度:☆)
Luogu P2872 [USACO07DEC]道路建设Building Roads(难度:★)
Luogu P2504 [HAOI2006]聪明的猴子(难度:★)
Luogu P2212 [USACO14MAR]浇地Watering the Fields(难度:★☆)
Author:Houge Date:2019.5.25
标签:pre,连通,int,Luogu,Kruskal,最小,生成,权值 来源: https://www.cnblogs.com/CSGOBESTGAMEEVER/p/10923099.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。