• [kuangbin带我飞]专题五--并查集 (9/14)


    1、

    Wireless Network

     POJ - 2236

     

    题意:有n台电脑,距离d以内的两台电脑维修后可以互相联系。一台电脑可以通过一对能够互相联系的电脑中的一个与另一个联系。给出每台电脑的坐标,再给出指令,维修一台电脑或是查询某两台电脑是否能够相互联系。对于每个查询指令输出答案。

    思路:很普通的并查集。

    代码:

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <cmath>
     4 using namespace std;
     5 const int N = 1010;
     6 struct node{
     7     double x, y;
     8 }c[N];
     9 int n, d, f[N], g[N];
    10 bool vis[N];
    11 
    12 int find(int x);
    13 void merge(int x, int y);
    14 bool check(node a, node b);
    15 void init();
    16 
    17 int main()
    18 {
    19     scanf("%d%d", &n, &d);
    20     init();
    21     for (int i=1;i<=n;i++)
    22     {
    23         scanf("%lf%lf", &c[i].x, &c[i].y);
    24     }
    25     char op;
    26     int num, cnt = 0, x, y;
    27     while (~scanf("%c", &op))
    28     {
    29         if (op == 'O')
    30         {
    31             scanf("%d", &num);
    32             if (vis[num] == false)
    33             {
    34                 for (int i=1;i<=cnt;i++)
    35                 {
    36                     if (check(c[g[i]], c[num]))
    37                         merge(g[i], num);
    38                 }
    39                 cnt++;
    40                 g[cnt] = num;
    41                 vis[num] = true;
    42             }
    43         }
    44         else if (op == 'S')
    45         {
    46             scanf("%d %d", &x, &y);
    47             if (find(x) == find(y))
    48                 puts("SUCCESS");
    49             else
    50                 puts("FAIL");
    51         }
    52     }
    53     return 0;
    54 }
    55 
    56 int find(int x)
    57 {
    58     return f[x] == x ? x : f[x] = find(f[x]);
    59 }
    60 void merge(int x, int y)
    61 {
    62     int fx = find(x);
    63     int fy = find(y);
    64     if (fx != fy)
    65     {
    66         f[fx] = fy;
    67     }
    68 }
    69 bool check(node a, node b)
    70 {
    71     double dis = sqrt( (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
    72     return dis <= d;
    73 }
    74 void init()
    75 {
    76     for (int i=1;i<=n;i++)
    77         f[i] = i;
    78 }
    View Code

     

    2、

    The Suspects

     POJ - 1611

     

    题意:学校里有n个学生,m个小组。小组中有一个人疑似得了冠状,那么全小组的人都可能得了。给出每个小组的成员,学生0疑似得了冠状,那么求有多少个人被怀疑。

    思路:比第一题更普通的并查集。

    代码:

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 using namespace std;
     5 const int N = 3e4+5;
     6 int f[N], n, m;
     7 
     8 int find(int x);
     9 void merge(int x, int y);
    10 void init();
    11 
    12 int main()
    13 {
    14     while (scanf("%d %d", &n, &m) && (n || m))
    15     {
    16         init();
    17         for (int i=1;i<=m;i++)
    18         {
    19             int k, y;
    20             scanf("%d", &k);
    21             scanf("%d", &y);
    22             for (int j=2;j<=k;j++)
    23             {
    24                 int x;
    25                 scanf("%d", &x);
    26                 merge(y, x);
    27             }
    28         }
    29         int cnt = 0;
    30         for (int i=0;i<n;i++)
    31         {
    32             if (find(i) == 0)
    33                 cnt++;
    34         }
    35         if (m == 0) cnt = 1;
    36         printf("%d
    ", cnt);
    37     }
    38     return 0;
    39 }
    40 
    41 int find(int x)
    42 {
    43     return f[x] == x ? x : f[x] = find(f[x]);
    44 }
    45 void merge(int x, int y)
    46 {
    47     int fx = find(x);
    48     int fy = find(y);
    49     if (fx != fy)
    50         f[max(fx, fy)] = min(fx, fy);
    51 
    52 }
    53 void init()
    54 {
    55     for (int i=0;i<n;i++)
    56         f[i] = i;
    57 }
    View Code

     

    3、

    How Many Tables

     HDU - 1213 
     
    题意:太直白了我都不想翻译了。
    思路:比第二题输入更普通的并查集。
    代码:
     1 #include <cstdio>
     2 #include <cstring>
     3 using namespace std;
     4 const int N = 1010;
     5 int T, n, m, f[N];
     6 bool vis[N];
     7 
     8 int find(int x);
     9 void merge(int x, int y);
    10 void init();
    11 
    12 int main()
    13 {
    14     scanf("%d", &T);
    15     while (T--)
    16     {
    17         memset(vis, 0, sizeof(vis));
    18         scanf("%d %d", &n, &m);
    19         init();
    20         for (int i=1;i<=m;i++)
    21         {
    22             int x, y;
    23             scanf("%d %d", &x, &y);
    24             merge(x, y);
    25         }
    26         int cnt = 0;
    27         for (int i=1;i<=n;i++)
    28         {
    29             int k = find(i);
    30             if (vis[k] == false)
    31             {
    32                 vis[k] = true;
    33                 cnt++;
    34             }
    35         }
    36         printf("%d
    ", cnt);
    37     }
    38     return 0;
    39 }
    40 
    41 int find(int x)
    42 {
    43     return f[x] == x ? x : f[x] = find(f[x]);
    44 }
    45 void merge(int x, int y)
    46 {
    47     int fx = find(x);
    48     int fy = find(y);
    49     if (fx != fy)
    50         f[fx] = fy;
    51 }
    52 void init()
    53 {
    54     for (int i=1;i<=n;i++)
    55         f[i] = i;
    56 }
    View Code

    哟,这不并查集吗?几天不见,这么拉了?

     

    4、

    How Many Answers Are Wrong

     HDU - 3038 

     我错了,并查集哥

    题意:给出n和m,再给出m次l, r, num, 表示在一个数组中a[l]+...+a[m]的值为num。要求判断有几句语句是错误的。
    思路:实在没有思路,于是翻了kuangbin的博客。看到了之间的量化关系就懂了。其实这题是很常见的并查集题型,只是题目有点抽象。只要把给出的语句理解为一个前缀和数组,sum[r] - sum[l - 1] = num。于是就可以用带权并查集做了。用rela数组记录当前节点与父结点之间的差值。
    代码:
     1 #include <cstdio>
     2 #include <cstring>
     3 using namespace std;
     4 const int N = 2e5+5;
     5 int f[N], rela[N], n, m;
     6 
     7 void init();
     8 void merge(int x, int y, int num);
     9 int find(int x);
    10 bool check(int x, int y, int num);
    11 
    12 int main()
    13 {
    14     while (~scanf("%d %d", &n, &m))
    15     {
    16         init();
    17         int l, r, num, cnt = 0;
    18         for (int i=1;i<=m;i++)
    19         {
    20             scanf("%d %d %d", &l, &r, &num);
    21             if (check(l-1, r, num))
    22             {
    23                 merge(l-1, r, num);
    24             }
    25             else
    26                 cnt++;
    27 
    28         }
    29         printf("%d
    ", cnt);
    30     }
    31     return 0;
    32 }
    33 
    34 void init()
    35 {
    36     for (int i=1;i<=n;i++)
    37         f[i] = i, rela[i] = 0;
    38 }
    39 void merge(int x, int y, int num)
    40 {
    41     int fx = find(x);
    42     int fy = find(y);
    43     if (fx != fy)
    44     {
    45         f[fy] = fx;
    46         rela[fy] = rela[x] + num - rela[y];
    47     }
    48 }
    49 int find(int x)
    50 {
    51     if (f[x] == x) return x;
    52     int temp = f[x];
    53     f[x] = find(f[x]);
    54     rela[x] = rela[x] + rela[temp];
    55     return f[x];
    56 }
    57 bool check(int x, int y, int num)
    58 {
    59     int fx = find(x);
    60     int fy = find(y);
    61     if (fx != fy)
    62         return true;
    63     return num == (rela[y] - rela[x]);
    64 }
    View Code

    总结:似乎只要这种题型,能量化给出语句之间的关系就可以用带权并查集做了。带权并查集在维护关系数组rela时

      在find()函数中,要维护x和x最终的父结点中间每一个节点的权值,这里用的是每个节点本身的权值来更新。

      在merge()函数中,要维护fx 和 fy其中一个变成子节点的节点的权值。需要rela[l], rela[r], num。其中num就是量化的关系。

     

    5、

     

    食物链

     POJ - 1182 

    题意:和上题一样是找错误语句的个数。关系只有吃和不吃。

    思路:也是先量化关系,再利用带权并查集。

    代码:

     

     1 #include <cstdio>
     2 #include <cstring>
     3 using namespace std;
     4 const int N = 5e4+5;
     5 int n, k, d, x, y, f[N], rela[N];
     6 
     7 inline int read();
     8 int find(int x);
     9 void merge(int x, int y, int d);
    10 bool check(int c, int x, int y);
    11 void init();
    12 
    13 int main()
    14 {
    15     n = read(); k = read();
    16     init();
    17     int cnt = 0;
    18     for (int i=1;i<=k;i++)
    19     {
    20         d = read(); x = read(); y = read();
    21         d--;
    22         if (check(d, x, y))
    23             merge(x, y, d);
    24         else
    25             cnt++;
    26     }
    27     printf("%d
    ", cnt);
    28     return 0;
    29 }
    30 
    31 void init()
    32 {
    33     for (int i=1;i<=n;i++)
    34         f[i] = i, rela[i] = 0;
    35 }
    36 bool check(int d, int x, int y)
    37 {
    38     if (x > n || y > n || (d == 2 && x == y))
    39         return false;
    40     if (find(x) == find(y))
    41         return ((rela[x] - rela[y] + 3) % 3 ) == d;
    42     else
    43         return true;
    44 }
    45 int find(int x)
    46 {
    47     if (x == f[x]) return x;
    48     int t = f[x];
    49     f[x] = find(f[x]);
    50     rela[x] = (rela[x] + rela[t]) % 3;
    51     return f[x];
    52 }
    53 void merge(int x, int y, int d)
    54 {
    55     int fx = find(x);
    56     int fy = find(y);
    57     if (fx != fy)
    58     {
    59         f[fx] = fy;
    60         rela[fx] = (rela[y] - rela[x] + d + 3) % 3;
    61     }
    62 }
    63 
    64 inline int read()
    65 {
    66     int x = 0, w = 1;
    67     char ch = 0;
    68     while (ch < '-')
    69     {
    70         if (ch == '-')
    71             w = -1;
    72         ch = getchar();
    73     }
    74     while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    75     return x * w;
    76 }
    View Code

     

     

     

    总结:我是先做的这一题再做的第4题,这一题时对带权并查集认识还不深。所以也是看了别人的博客,这里面不但讲了带权并查集,也列了表描述这一题的关系,看题解还是推荐看这个。

     

    6、

    True Liars

     POJ - 1417 

    题意:一群天使和恶魔住在一个岛上,天使只说真话,恶魔只说假话。向xi号人问第yi号人是否是天使并得到回答。问能否得到哪一部分人是天使,只能有唯一解。

    思路:思考一下可知,若回答为yes则两人为同类,回答为no则两人为异类。用带权并查集可以将他们分为一个个集合,每个集合中有两部分人。然后将这些集合组成起来看是否能得到解。用动态规划可以得出解的最大数量。如果不是1,则打印no。如果是1,那我也不知道怎么办了。这题太考验编码能力了,思路倒是比较简单。

    半成品代码:

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <vector>
     4 using namespace std;
     5 const int N = 1010, M = 305;
     6 int n, p1, p2, f[M], rela[M], dp[M][M], a[M], b[2][M];
     7 bool vis[M];
     8 int find(int x);
     9 void init();
    10 
    11 int main()
    12 {
    13     while (~scanf("%d %d %d", &n, &p1, &p2) && n && p1 && p2)
    14     {
    15         init();
    16         while (n--)
    17         {
    18             int x, y; char s[10];
    19             scanf("%d %d %s", &x, &y, s);
    20             int flag = (s[0] == 'y');
    21             int fx = find(x), fy = find(y);
    22             if (fx != fy)
    23             {
    24                 f[fy] = fx;
    25                 rela[fy] = (rela[x] + rela[y] + flag) % 2;
    26             }
    27         }
    28         int cnt = 0;
    29         memset(vis, 0, sizeof(vis));
    30         memset(b, 0, sizeof(b));
    31         for (int i=1;i<=p1+p2;i++)
    32         {
    33             if (vis[f[i]] == 0)
    34             {
    35                 vis[f[i]] = true;
    36                 a[++cnt] = f[i];
    37             }
    38             b[rela[i]][f[i]]++;
    39         }
    40         for (int i=1;i<=cnt;i++)
    41         {
    42             for (int j=p1+p2;j>=1;j--)
    43             {
    44                 if (j >= b[0][a[i]] && dp[i-1][j-b[0][a[i]]])
    45                     dp[i][j] += dp[i-1][j-b[0][a[i]]];
    46                 if (j >= b[1][a[i]] && dp[i-1][j-b[1][a[i]]])
    47                     dp[i][j] += dp[i-1][j-b[1][a[i]]];
    48             }
    49         }
    50         if (dp[cnt][p1] != 1)
    51         {
    52             puts("no");
    53             continue;
    54         }
    55         
    56     }
    57     return 0;
    58 }
    59 
    60 int find(int x)
    61 {
    62     if (f[x] == x)
    63         return x;
    64     int temp = f[x];
    65     f[x] = find(f[x]);
    66     rela[x] = (rela[x] + rela[temp]) % 2;
    67     return f[x];
    68 }
    69 void init()
    70 {
    71     for (int i=1;i<=p1+p2;i++)
    72         f[i] = i, rela[i] = 0;
    73 }
    View Code

    总结:我要好好去提升自己的编码能力了,等我足够强了会回来补这题的(大概)。

     

    7、

    Supermarket

     POJ - 1456

    题意:超市有n样东西要卖,每个东西都有自己的价值和过期时间。一天只能卖一样东西,过期后无法再卖。问能卖到最多多少钱?

    思路1:贪心:

      对物品以价值从大到小排序。按顺序从价值最大的物品到价值最小的物品,对每一个物品从过期时间开始向前寻找没有卖东西的日子,在可卖出的最晚时间卖出。虽然是O(n^2)也可以AC。

    代码1:

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 using namespace std;
     5 const int N = 1e4+5;
     6 struct product
     7 {
     8     int p, d;
     9 } a[N];
    10 int n;
    11 bool vis[N];
    12 
    13 bool cmp(product a, product b)
    14 {
    15     return a.p > b.p;
    16 }
    17 
    18 int main()
    19 {
    20     while (~scanf("%d", &n))
    21     {
    22         memset(vis, 0, sizeof(vis));
    23         for (int i=1; i<=n; i++)
    24             scanf("%d %d", &a[i].p, &a[i].d);
    25         sort(a+1, a+1+n,cmp);
    26         int ans = 0;
    27         for (int i=1; i<=n; i++)
    28         {
    29             if (vis[a[i].d])
    30             {
    31                 for (int j=a[i].d - 1; j >= 1; j--)
    32                 {
    33                     if (!vis[j])
    34                     {
    35                         vis[j] = true;
    36                         ans += a[i].p;
    37                         break;
    38                     }
    39                 }
    40             }
    41             else
    42             {
    43                 ans += a[i].p;
    44                 vis[a[i].d] = true;
    45             }
    46         }
    47         printf("%d
    ", ans);
    48     }
    49     return 0;
    50 }
    View Code 

    思路2:用并查集优化思路1的代码。

      用f数组记录最近的空闲日期。当前日期被使用后,将其向前移,寻找最近的空闲日期,并记录。说是并查集优化,其实是优化了路径。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 using namespace std;
     5 const int N = 1e4+5;
     6 struct product
     7 {
     8     int p, d;
     9 } a[N];
    10 int n;
    11 int f[N];
    12 
    13 bool cmp(product a, product b)
    14 {
    15     return a.p > b.p;
    16 }
    17 int find(int x)
    18 {
    19     return f[x] == -1 ? x : f[x] = find(f[x]);
    20 }
    21 
    22 int main()
    23 {
    24     while (~scanf("%d", &n))
    25     {
    26         memset(f, -1, sizeof(f));
    27         for (int i=1;i<=n;i++)
    28             scanf("%d %d", &a[i].p, &a[i].d);
    29         sort(a+1, a+n+1, cmp);
    30         int ans = 0;
    31         for (int i=1;i<=n;i++)
    32         {
    33             int d = find(a[i].d);
    34             if (d > 0)
    35             {
    36                 f[d] = d - 1;
    37                 ans += a[i].p;
    38             }
    39         }
    40         printf("%d
    ", ans);
    41     }
    42     return 0;
    43 }
    View Code

     

    思路3:动态规划,人称小贪心。耗时大大增加

      状态:dp[j]表示卖出j件物品得到的价值最大值。

      状态转移方程:dp[j] = max(dp[j], dp[j-1]+v[i])。

      细节:每样东西可卖可不卖,就是01背包。加上时间限制的话,第二层循环就在d[i]...1即可。

    代码:

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 using namespace std;
     5 const int N = 1e4+5;
     6 struct product
     7 {
     8     int p, d;
     9 } a[N];
    10 int n, dp[N];
    11 
    12 bool cmp(product a, product b)
    13 {
    14     return a.d < b.d;
    15 }
    16 
    17 int main()
    18 {
    19     while (~scanf("%d", &n))
    20     {
    21         memset(dp, 0, sizeof(dp));
    22         for (int i=1;i<=n;i++)
    23             scanf("%d %d", &a[i].p, &a[i].d);
    24         sort(a+1, a+1+n, cmp);
    25         int ans = 0;
    26         for (int i=1;i<=n;i++)
    27             for (int j=a[i].d;j > 0;j--)
    28             {
    29                 dp[j] = max(dp[j], dp[j-1]+a[i].p), ans = max(ans, dp[j]);
    30             }
    31         printf("%d
    ", ans);
    32     }
    33     return 0;
    34 }
    View Code

     

    8、

    Parity game POJ - 1733 (离散化+带权并查集)

    9、

    Navigation Nightmare POJ - 1984(带权并查集)

    10、

    A Bug's Life

     POJ - 2492

    代码:

     1 #include <cstdio>
     2 #include <cstring>
     3 using namespace std;
     4 const int N = 2020;
     5 int n, m, f[N], rela[N];
     6 bool flag;
     7 
     8 void init();
     9 int find(int x);
    10 void solve(int x, int y);
    11 
    12 int main()
    13 {
    14     int T;
    15     scanf("%d", &T);
    16     for (int I=1;I<=T;I++)
    17     {
    18         flag = false;
    19         scanf("%d%d", &n, &m);
    20         init();
    21         for (int i=1;i<=m;i++)
    22         {
    23             int x, y;
    24             scanf("%d %d", &x, &y);
    25             if (flag) continue;
    26             solve(x, y);
    27         }
    28         printf("Scenario #%d:
    ", I);
    29         if (flag)
    30             printf("Suspicious bugs found!
    ");
    31         else
    32             printf("No suspicious bugs found!
    ");
    33         puts("");
    34     }
    35     return 0;
    36 }
    37 
    38 void init()
    39 {
    40     for (int i=1;i<=n;i++)
    41         f[i] = i, rela[i] = 0;
    42 }
    43 int find(int x)
    44 {
    45     if (f[x] == x) return x;
    46     int temp = f[x];
    47     f[x] = find(f[x]);
    48     rela[x] = (rela[x] + rela[temp]) % 2;
    49     return f[x];
    50 }
    51 void solve(int x, int y)
    52 {
    53     int fx = find(x), fy = find(y);
    54     if (fx != fy)
    55     {
    56         f[fy] = fx;
    57         rela[fy] = (rela[x] + rela[y] + 1) % 2;
    58     }else
    59     {
    60         if (rela[x] == rela[y])
    61             flag = true;
    62     }
    63 }
    View Code

    这题离谱,这个代码700+ms,用了快读1500+ms,poj上却有0ms的。不懂=。=

    11、

  • 相关阅读:
    HDU 1150 Machine Schedule(二分匹配最小点覆盖)
    CodeForces 748F Santa Clauses and a Soccer Championship
    CodeForces 748E Santa Claus and Tangerines(二分)
    CodeForces 748D Santa Claus and a Palindrome (贪心+构造)
    POJ 3657 Haybale Guessing(二分+并查集)
    【JZOJ5773】简单数学题【数论,数学】
    【洛谷P4085】Haybale Feast【分块】
    【洛谷P4085】Haybale Feast【分块】
    【洛谷P4085】Haybale Feast【分块】
    【洛谷P4212】外太空旅行【随机】【贪心】
  • 原文地址:https://www.cnblogs.com/FantaDevourer/p/12740754.html
Copyright © 2020-2023  润新知