先前在csdn上写了几篇博客 但受不了它中网站的速度 还是来这吧 也不准备换家了 好好地开始吧..
昨天 晓爷 性神他们去了省赛 结果还不错... 说好的请桶呢?
我还是要继续啊 虽然有时会 力不从心 但 总是生活充满了挑战
今天 讲下Kruskal算法的基础应用. 本篇还未涉及到用堆 优化 等以后我掌握了再来写
题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=38
先讲下 对于Kruskal算法 当作我自己的温习 和 偶然你看到时的一点分享。
Kruskal:它考虑的是图中 边权值的大小比较 所以 这个算法的效率和边数的多少密切相关 我建议在稀疏图的时候用Kruskal 在稠密图的时候使用Prim
至于 稀疏图 稠密图 顾名思义 即 边和顶点 数的 相对大小比较
一般 我们采取用c++自带的sort(在 algorithm头文件中)进行对边的权值的排序
我们想要的结果是在n个顶点的图中 通过连接 n-1 条的边来使整个图连通 即我们最终要得到的是一个连通块
首先 我们可以将每个点看成一个连通分量 然后先根据排序的结果 将边的权值最小的首先加入MST(最小生成树所在的集合), 每条边都有2个顶点
我们需要进行的核心步骤是 判断这2个点顶点的连通性 即 查询 与 合并操作
因为 如果2个点是在同一个连通分量中 我们将他们合并 则会成环 这肯定不是我们要的结果
这里 就用到了 强大的 并查集(Unio-Find-Set)如果 你对并查集还没有一个清晰的概念 你可以去看下其它大牛的博客
至此 Kruskal 算法的思想大概也讲完了 接下来 就是具体的实现方式了 一般我们采用 邻接表 或 邻接矩阵 来存储图
通常来说 邻接表的效率更高 但 邻接矩阵写起来更简单 还是看个人习惯和具体题目吧
我先给出这题的Kruskal实现方式 会在下一篇 我讲Prim的时候 给出Prim的实现方式

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 6 #define big 125000 7 int side[big]; 8 int father[550]; 9 int begin[big]; 10 int end[big]; 11 int value[big]; 12 13 int n,m; 14 15 int find(int x) 16 { 17 return x==father[x]?x:father[x]=find(father[x]);//寻找父节点 18 } 19 int cmp(const int i,const int j) 20 { 21 return value[i]<value[j];//间接排序 你也可以用结构体来存储顶点和边 就不需要使用间接排序 22 } 23 24 void set(int n,int m) 25 { 26 for(int i=0;i<n;i++) 27 father[i]=i; 28 for(int i=0;i<m;i++) 29 side[i]=i; 30 } 31 32 int Kruskal() 33 { 34 int ans=0; 35 set(n,m);//初始化 36 sort(side,side+m,cmp);//对进行排序 37 for(int i=0;i<m;i++) 38 { 39 int e=side[i];//这条边在权值中的序号 40 int x=find(begin[e]);//边的一端 41 int y=find(end[e]);//边的另一端 42 if(x!=y) 43 { 44 ans+=value[e]; 45 father[x]=y;//合并 46 } 47 } 48 return ans; 49 } 50 51 int main() 52 { 53 int i,t,cost,min; 54 while(~scanf("%d",&t)) 55 { 56 while(t--) 57 { 58 min=9999999; 59 scanf("%d %d",&n,&m); 60 for(i=0;i<m;i++) 61 { 62 scanf("%d %d %d",&begin[i],&end[i],&value[i]); 63 } 64 int sum=Kruskal(); 65 for(i=0;i<n;i++) 66 { 67 scanf("%d",&cost); 68 if(cost<min) 69 min=cost; 70 } 71 printf("%d ",min+sum); 72 } 73 } 74 }
这里 我想再单独的多提下 间接排序 因为 我当初第一次接触它的时候 就很疑惑 我想通过下面的一段代码的测试结果 来多给你点感觉

1 #include <iostream> 2 #include <algorithm> 3 using namespace std; 4 5 int a[20]={0,1,2,3,4,5,6,7};//边的序号 6 int b[20]={9,10,11,8,12,13,15,14};//边权值大小 7 int cmp(const int i,const int j) 8 { 9 return b[i]<b[j]; 10 } 11 12 int main() 13 { 14 sort(a,a+8,cmp); 15 for(int i=0; i<8; i++) 16 printf("%d ", a[i] ); 17 printf(" "); 18 for(int i=0; i<8; i++) 19 printf("%d ", b[i] ); 20 return 0; 21 }
希望 你也可以在评论区 与我进行 交流
PS: 我只是个初学者 任何错的地方 欢迎指出 让我们共同进步。。。