图论入门-Kruskal算法

1943: 最优布线问题

Time Limit: 1 Sec  Memory Limit: 128 MB   64bit IO Format: %lld
Submitted: 33  Accepted: 19
[Submit][Status][Web Board]

Description

   学校有n台计算机,为了方便数据传输,现要将它们用数据线连接起来。两台计算机被连接是指它们间有数据线连接。由于计算机所处的位置不同,因此不同的两台计算机的连接费用往往是不同的。
    当然,如果将任意两台计算机都用数据线连接,费用将是相当庞大的。为了节省费用,我们采用数据的间接传输手段,即一台计算机可以间接的通过若干台计算机(作为中转)来实现与另一台计算机的连接。
  现在由你负责连接这些计算机,任务是使任意两台计算机都连通(不管是直接的或间接的)。
 

Input

多组测试数据。
第一行为整数n(2<=n<=100),表示计算机的数目。
此后的n行,每行n个整数。第x+1行y列的整数表示直接连接第x台计算机和第y台计算机的费用。
 

Output

输出一个整数,表示最小的连接费用。
 

Sample Input 

 
3
0 1 2
1 0 1
2 1 0

Sample Output

2

题目连接:http://acm.wust.edu.cn/problem.php?id=1943&soj=0

  这个题是个很基础的最小生成树的题, 最小生成树的题有Prime(普利姆)算法和Kruskal(克鲁斯卡尔)算法可以解决, 个人推荐Kruskal算法, 很简单。 下面挂上Kruskal算法的代码。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<iomanip>
 4 #include<queue>
 5 #include<map>
 6 #include<set>
 7 #include<stack>
 8 #include<list>
 9 #include<algorithm>
10 #include<cmath>
11 #include<cstring>
12 #include<string>
13 #include<sstream>
14 #include<fstream>
15 #define go(t) int t;    cin>>t;     while(t--)
16 #define ll long long
17 #define rep(i, a, n) for(int i = a; i < n; i++)
18 #define res(i, a, n) for(int i = a - 1; i >= n; i--)
19 using namespace std;
20 const int inf = -1;
21 const int mod = 1e9 + 7;
22 const double pi = acos(-1);
23 const int N = 120;
24 
25 //创建结构体存数据, 以及动手写一个比较大小的函数
26 /*******************************************/
27 /**/struct node{
28 /**/    int x, y, z;
29 /**/}a[N * N];
30 /**/
31 /**/bool com(node aa, node bb){
32 /**/    return aa.z < bb.z;
33 /**/}
34 /*******************************************/
35 
36 //但是我按下面这种写法来居然会报错, 是我还是太年轻吗
37 /*
38 struct node{
39 int x, y, z;
40 bool operator < (node &a){
41     return z < a.z;
42 }
43 }a[N * N];
44 */
45 
46 //然后接下就是并查集的操作了
47 int fa[N], n, m;
48 
49 //初始化, 先令fa[i] = i, 这里不需要val数组来存放权值, 所以就没写
50 void init(){
51     rep(i, 0, n + 1)    fa[i] = i;
52 }
53 
54 //查找根节点, 还是和并查集一样, 注意fa[x] = , 上面的init写了记得用上, 别像我一样, 老是忘了用
55 int Find(int x){
56     return x == fa[x] ? x : fa[x] = Find(fa[x]);
57 }
58 
59 int main(){
60     while(cin >> n){
61         init();
62         int idx = 1;    //加个idx方便数据的输入
63         rep(i, 1, n + 1){
64             rep(j, 1, n + 1){
65                 a[idx].x = i;
66                 a[idx].y = j;
67                 cin >> a[idx++].z;
68             }
69         }
70         sort(a + 1, a + n * n + 1, com);    
71         //排个序, 没有重载小于号的话, 可以在sort加上第三个参数, 也就是比较函数
72         
73         //精彩的部分来了, ans是answer的缩写, 即为最终答案, tot是选择连接的线路的条数
74         int ans = 0, tot = 0;
75         
76         rep(i, 1, n * n + 1){
77             int x = a[i].x, y = a[i].y;
78             x = Find(x);    y = Find(y);
79             
80             if(x != y){
81                 fa[x] = y;      //到这一步为止是并查集的操作
82                 
83                 //解释一下这里, 连接n个电脑只需要n - 1条线路就可以了
84                 //所以我们选择最短的n - 1条线路, 这也是之前要先排序一下的目的
85                 //但是这n - 1条线路连接起来的图形不能有环, 所以x不能等于y
86                 ans += a[i].z;
87                 tot++;
88             }
89             //如果已经选好了n - 1条线路, 就可以退出了
90             if(tot == n - 1)    break;
91         }
92         //由题意可得, 一定会有解, 所以直接输出结果就好了
93         cout << ans << endl;
94     }
95     return 0;
96 }
View Code

  对于克鲁斯卡尔算法, 我个人感觉是贪心算法和并查集的结合, 操作起来很简单, 理解起来也毫无困难。 对于新手来说, 应该是非常友好的一种算法了。

猜你喜欢

转载自www.cnblogs.com/123zhh-helloworld/p/9102617.html