最小生成树之Kruskal的应用

先前在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 }        
View Code

这里  我想再单独的多提下 间接排序 因为 我当初第一次接触它的时候 就很疑惑 我想通过下面的一段代码的测试结果 来多给你点感觉

 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 }
View Code

希望 你也可以在评论区 与我进行 交流

PS: 我只是个初学者 任何错的地方 欢迎指出 让我们共同进步。。。

just follow your heart
原文地址:https://www.cnblogs.com/radical/p/3662411.html