zoukankan      html  css  js  c++  java
  • 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 }

        

  • 相关阅读:
    UVa 1349 (二分图最小权完美匹配) Optimal Bus Route Design
    UVa 1658 (拆点法 最小费用流) Admiral
    UVa 11082 (网络流建模) Matrix Decompressing
    UVa 753 (二分图最大匹配) A Plug for UNIX
    UVa 1451 (数形结合 单调栈) Average
    UVa 1471 (LIS变形) Defense Lines
    UVa 11572 (滑动窗口) Unique Snowflakes
    UVa 1606 (极角排序) Amphiphilic Carbon Molecules
    UVa 11054 Wine trading in Gergovia
    UVa 140 (枚举排列) Bandwidth
  • 原文地址:https://www.cnblogs.com/windysai/p/3298819.html
Copyright © 2011-2022 走看看