[POJ]并查集三连发

整理一下最近的题解,这是三个并查集的题目,分别是:

POJ 1182 食物链
POJ 1611 The Suspects
POJ 2524 Ubiquitous Religions

POJ 1182 食物链

这个题有一个很棒的套路,那就是用一个并查集来维护三个集合内的元素,可以使这个并查集扩大n倍,然后按照元素是属于哪一个集合来进行运算,放入并查集对应范围内的对应位置。代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 0x7ffffff;
int pre[maxn];

int find(int x) {
    return x == pre[x] ? x : pre[x] = find(pre[x]);
}

void unite(int x, int y) {
    x = find(x);
    y = find(y);
    if(x != y) {
        pre[y] = x;
    }
}

int main() {
    int N, K, D, x, y;
    scanf("%d %d", &N, &K);
    int mm = N * 3;
    for(int i = 0; i < mm; i++) {
        pre[i] = i;
    }
    int ans = 0;
    
    while(K--) {
        scanf("%d %d %d", &D, &x, &y);
        if(x > N || y > N) {
            ans++;
            continue;
        }
        if(D == 1) {    //同类
            if(find(x) == find(y+N) || find(x) == find(y+2*N)) {
                ans++;  //矛盾
            }
            else {
                unite(x, y);        //放入A
                unite(x+N, y+N);    //放入B
                unite(x+2*N, y+2*N);//放入C
            }
        }
        else if(D == 2) {   //不同类
            if(find(x) == find(y) || find(x) == find(y+2*N)) {
                ans++;  //矛盾
            }
            else {
                unite(x, y+N);      //x放入A,y放入B
                unite(x+N, y+2*N);  //x放入B,y放入C
                unite(x+2*N, y);    //x放入B,y放入A
            }
        }
    }
    printf("%d
", ans);
}
View Code

POJ 1611 The Suspects

这道题可以在输入的时候进行处理,单独读入一个元素后,再循环读入剩下的,再与每一个元素的前者进行合并操作(如果不是同一个father)。在并查集的unite操作中来动态统计合并到某两个元素的时候此时的suspects的数量,这样的话,只需要找到0号的father就可以找到suspects的总人数了(默认0是患病的)。代码如下:

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include <cstdlib>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <iostream>
 6 
 7 using namespace std;
 8 
 9 const int maxn = 30010;
10 int father[maxn];
11 int num[maxn];
12 int n, m;
13 
14 void init() {
15     for (int i = 0; i < maxn; i++) {
16         father[i] = i;
17         num[i] = 1;
18     }
19 }
20 int find(int x) {
21     return x == father[x] ? x : father[x] = find(father[x]);
22 }
23 void unite(int x, int y) {
24     x = find(x);
25     y = find(y);
26     if(x != y) {    
27         father[x] = y;
28         num[y] += num[x];
29     }
30 }
31 
32 int main(int argc, char *argv[]) {
33     while (~scanf("%d %d", &n, &m) && n + m) {
34         init();
35         int group;
36         int tmp[maxn];
37         int ans = 0;
38         for (int ii = 0; ii < m; ii++) {
39             memset(tmp, 0, sizeof(tmp));
40             scanf("%d", &group);
41             scanf("%d", &tmp[0]);
42             for (int i = 1; i < group; i++) {
43                 scanf("%d", &tmp[i]);
44                 if (father[tmp[i]] != father[tmp[i - 1]]) {
45                     unite(tmp[i], tmp[i - 1]);
46                 }
47             }
48         }
49         // for (int i = 0; i < n; i++) {
50         //     if (father[i] == 0) {
51         //         ans++;
52         //     }
53         // }
54         printf("%d
", num[find(0)]);
55     }
56     return 0;
57 }
View Code

POJ 2524 Ubiquitous Religions

额外加一个vis来判断某信仰是否被遍历到,利用并查集“合并过的两个元素其中一个元素的father必定不是本身”这一性质统计一共有多少个集合。代码如下:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 const int maxn = 50010;
 8 int pre[maxn];
 9 int vis[maxn];
10 int n, m;
11 
12 int find(int x) {
13     return x == pre[x] ? x : pre[x] = find(pre[x]);
14 }
15 
16 void unite(int x, int y) {
17     x = find(x);
18     y = find(y);
19     if(x != y) {
20         pre[y] = x;
21     }
22 }
23 inline void init() {
24     for(int i = 0; i < maxn; i++) {
25         pre[i] = i;
26     }
27 }
28 
29 int main() {
30     int kase = 1;
31     while(~scanf("%d %d", &n, &m) && n+m) {
32         init();
33         memset(vis, 0, sizeof(vis));
34         int a, b;
35         int cnt = 0;
36         for(int i = 0; i < m; i++) {
37             scanf("%d %d", &a, &b);
38             vis[a] = 1; 
39             vis[b] = 1;
40             unite(a, b);
41         }
42         int k = 0;
43         // for(int i = 1; i <= n; i++) {
44         //  cout << pre[i] << " ";
45         // }
46         // cout << endl;
47         for(int i = 1; i <= n; i++) {
48             if(vis[i] && pre[i] == i) {
49                 cnt++;
50             }
51             if(!vis[i]) {
52                 cnt++;
53             }
54         }
55         printf("Case %d: %d
", kase++, cnt);
56     }
57     return 0;
58 }
View Code
原文地址:https://www.cnblogs.com/kirai/p/4769265.html