poj 1611 The Suspects 解题报告

题目链接:http://poj.org/problem?id=1611

题意:给定n个人和m个群,接下来是m行,每行给出该群内的人数以及这些人所对应的编号。需要统计出跟编号0的人有直接或间接关系的人数总共有多少。

       这又是一条并查集的应用。难点是如何统计出与0有关系的总人数,即包含0的集合内元素的总个数。我的方法是用了两次merge,第一次merge单纯地将同一群内的元素连边,当然该群内的元素的祖先有可能是别的群内的元素,连边的规则是大的元素指向小的元素;第二次merge则把第一次筛选出来的集合中将元素与它的祖先再合并。目的是为了每一个集合内的元素都指向同一个代表。

       以第一个数据测试来说,第二行的 2  1  2,把编号1和2的人连边,此时p[1] = 1, p[2] = 1;

第四行的  2   0   1,把编号0和1的人连边,此时p[0] = 0,p[1]  = 0;第五行的 2  99  2,结果p[99] = 0,p[2] = 1。(p[99] 指向元素2的祖先,而2的祖先1的祖先为0,因此p[99] = 0)。处理完问题出现了,第一次merge并不能使p[2]指向0,它还是保持1,因此第二次merge就有必要设置了。它使得p[2] = 0。由于代表为0的集合上所有的元素都指向0,因此最后统计哪些祖先是0的集合个数就是答案了。

 1 #include <iostream>
 2 using namespace std;
 3 
 4 const int maxn = 30000 + 5;
 5 int p[maxn];
 6 
 7 int find(int x)
 8 {
 9     while (x != p[x])
10         x = p[x];
11     return x;
12 }
13 
14 void merge(int x, int y)
15 {
16     int fx = find(x);
17     int fy = find(y);
18     if (fx > fy)        // 从一开始就保证大的元素指向小的元素,即大的元素的祖先是小的元素
19         p[fx] = fy;
20     else
21         p[fy] = fx;
22 }
23 
24 void merge1(int x, int y)      // 第二次merge很关键
25 {
26     int fx = find(x);
27     int fy = find(y);
28     if (fx > fy)        // 数组下标与第一次merge是不同的!!!
29         p[x] = fy;
30     else
31         p[y] = fx;
32 }
33 
34 int main()
35 {
36     int i, j, n, m, t, x, k;
37     while (scanf("%d%d", &n, &m) != EOF && (m || n))
38     {
39         if (m == 0)     // 特殊情况没有群,此时只有0这个学生是嫌疑犯
40             printf("1\n");
41         else
42         {
43             for (i = 0; i < n; i++)
44             {
45                 p[i] = i;
46             }
47             for (i = 0; i < m; i++)
48             {
49                 scanf("%d", &k);  
50                 scanf("%d", &t);  // 每组中第一个人要单独输入,为了下面的merge操作方便     
51                 for (j = 1; j < k; j++)
52                 {
53                     scanf("%d", &x);
54                     merge(t, x);
55         //            printf("p[%d] = %d, p[%d] = %d\n", t, p[t], x, p[x]);
56                     t = x;
57                 }
58             }
59             int cnt = 1;      // 编号为0这个人
60             for (i = 1; i < n; i++)
61             {
62                 if (p[i] != i)    // 第一轮merge后的再一次处理
63                 {
64                     merge1(p[i], i);
65             //        printf("p[%d] = %d\n", i, p[i]); 
66                     if (p[i] == 0)
67                         cnt++;
68                 }
69             }
70             printf("%d\n", cnt);
71         }
72     }
73     return 0;
74 }

 

 

    

 

posted @ 2013-09-03 15:29  windysai  阅读(310)  评论(0编辑  收藏  举报