1. 程式人生 > 實用技巧 >POJ 2421 Constructing Roads (kruskal + 並查集)

POJ 2421 Constructing Roads (kruskal + 並查集)

題目連結:POJ 2421

Describe:
There are N villages, which are numbered from 1 to N, and you should build some roads such that every two villages can connect to each other. We say two village A and B are connected, if and only if there is a road between A and B, or there exists a village C such that there is a road between A and C, and C and B are connected.

We know that there are already some roads between some villages and your job is the build some roads such that all the villages are connect and the length of all the roads built is minimum.
Input:
The first line is an integer N (3 <= N <= 100), which is the number of villages. Then come N lines, the i-th of which contains N integers, and the j-th of these N integers is the distance (the distance should be an integer within [1, 1000]) between village i and village j.

Then there is an integer Q (0 <= Q <= N * (N + 1) / 2). Then come Q lines, each line contains two integers a and b (1 <= a < b <= N), which means the road between village a and village b has been built.
Output:
You should output a line contains an integer, which is the length of all the roads to be built such that all the villages are connected, and this value is minimum.
Sample Input:
3
0 990 692
990 0 179
692 179 0
1
1 2
Sample Output:
179

題目大意:

有n個房子,有些房子之間已經存在路,問最少修多長的路可以保證所有房子連通。

解題思路:

題目給的兩個房子之間的距離就是邊的權重,使邊的權重最小,即最小生成樹,kruskal+並查集就可以解決(感覺kruskal和並查集好像分不開)。

AC程式碼:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <queue>
 5 #define rep(i,n) for(int i = 0;i < n;i++) // 從別人程式碼裡偷學過來的
 6 using namespace std;
 7 struct edge // 定義邊
 8 {
 9     int e,w,st,ed; // e表示這個邊是本來就存在還是新建的,w為權重,st和ed是邊的頂點
10     bool operator < (const edge &a) const // 比較函式
11     {
12         if(e != a.e) return e < a.e; // 此處注意,出現過的邊優先順序最高,即先比較e
13         else return w > a.w;
14     }
15 };
16 int g[100][100],d[100][100],arr[100],r[100]; // g為圖,d儲存距離,arr和r為並查集
17 int n,qq,x,y,ans,cnt;
18 int getfather(int x) // 以下兩個函式並查集常規操作
19 {
20     return arr[x] == x?x:(arr[x] = getfather(arr[x]));
21 }
22 void mergeset(int x, int y)
23 {
24     x = getfather(x); y = getfather(y);
25     if(x != y)
26     {
27         if(r[x] <= r[y]) arr[x] = y;
28         else arr[y] = x;
29         if(r[x] == r[y] && x != y) r[y]++;
30     }
31 }
32 int main()
33 {
34     ans = 0; // 初始化答案
35     cnt = 0; // pop邊時的計數器
36     memset(r,1,sizeof(r));
37     priority_queue<edge> q; // 針不戳,優先佇列針不戳
38     scanf("%d",&n);
39     rep(i,n) arr[i] = i; // 並查集初始化
40     rep(i,n)
41     {
42         rep(j,n)
43         {
44             scanf("%d",&d[i][j]); // 輸入距離
45         }
46     }
47     scanf("%d",&qq);
48     rep(i,qq)
49     {
50         scanf("%d%d",&x,&y); // 鄰接矩陣記錄邊
51         x--; y--; // 注意下標
52         g[x][y] = g[y][x] = 1;
53     }
54     rep(i,n)
55     {
56         for(int j = i+1; j < n; j++) // 畫個圖就可以看出來
57         {
58             edge tmp; // 插入邊
59             tmp.w = d[i][j];
60             if(g[i][j] == 1) tmp.e = 1; // 判斷是否為已經存在的邊
61             else tmp.e = 0;
62             tmp.st = i;
63             tmp.ed = j;
64             q.push(tmp); // 入對
65         }
66     }
67     while(cnt != (n-1)) // 生成樹有n-1條邊
68     {
69         edge tmp = q.top();
70         q.pop();
71         int t1,t2;
72         t1 = getfather(tmp.st);
73         t2 = getfather(tmp.ed);
74         if(t1 == t2) continue; // 判斷是否已經成一棵樹
75         mergeset(tmp.st,tmp.ed); // 不是一棵樹則合併
76         if(g[tmp.st][tmp.ed] == 0) ans += tmp.w; // 注意要加上新建的邊
77         cnt++;
78     }
79     printf("%d",ans);
80     return 0;
81 }