codevs 1069 关押罪犯 (并查集)

来源:互联网 发布:医学搜题软件 编辑:程序博客网 时间:2024/04/27 17:16

题意:有n名罪犯,m个关系,每个关系(u, v, w)表示犯人u和v如果关在同一个监狱之间会产生怨气值w,现在有两个监狱,问你怎么放置着n个犯人,犯人之间最大怨气值最小,求这个值。N≤ 20000,M≤ 100000


思路:(点击打开链接)维护罪犯在哪一个监狱不方便,我们可以维护某两个罪犯是不是在一个监狱。考虑到并查集的本职工作是维护某两点在一个集合,不能很好地处理不在一个集合的情况,通过保存某个点的“敌人”集合来代表和他不在一个监狱的罪犯,间接地实现维护某两点不在一个集合的情况。在加入关系的时候进行判断,如果某两点已经在一个集合,说明他们无论如何也安排不到不同的两个监狱了,输出仇恨值即可;如果不在一个集合,就将犯人和对方的敌人合并到一个集合。

(敌人的敌人就是朋友)


代码:

#include<bits/stdc++.h>using namespace std;typedef long long ll;const int maxn = 1e5+5;struct node{    int u, v, w;    bool operator <(const node &a) const    {        return w > a.w;    }}a[maxn];int pre[maxn], en[maxn], n, m;int Find(int x){    int r = x;    while(pre[r] != r) r = pre[r];    int i = x, j;    while(i != r)    {        j = pre[i];        pre[i] = r;        i = j;    }    return r;}void join(int x, int y){    int a = Find(x);    int b = Find(y);    if(a != b)        pre[b] = a;}int main(void){    while(cin >> n >> m)    {        for(int i = 1; i <= n; i++)            pre[i] = i, en[i] = 0;        for(int i = 1; i <= m; i++)            scanf("%d%d%d", &a[i].u, &a[i].v, &a[i].w);        sort(a+1, a+1+m);        int ans = 0;        for(int i = 1; i <= m; i++)        {            int x = a[i].u;            int y = a[i].v;            int fa = Find(x);            int fb = Find(y);            if(fa == fb)            {                ans = a[i].w;                break;            }            if(en[x]) join(y, en[x]);            else en[x] = y;            if(en[y]) join(x, en[y]);            else en[y] = x;        }        printf("%d\n", ans);    }    return 0;}


  1. #include<algorithm>  
  2. #include<iostream>  
  3. #include<cstdio>  
  4. #include<cstring>  
  5. using namespace std;  
  6. struct node  
  7. {  
  8.     int a , b , c;  
  9. };  
  10. const int N = 100000 + 50;  
  11. node man[N];  
  12. int p[N];  
  13. bool cmp(node a , node b)  
  14. {  
  15.     return a.c > b.c;  
  16. }  
  17. int find(int x)  
  18. {  
  19.     return x == p[x]?x:x=find(p[x]);  
  20. }  
  21. int main(int argc, char const *argv[])  
  22. {  
  23.     int n , m;  
  24.     cin >> n >> m;  
  25.     for (int i = 0; i < m; ++i)  
  26.         scanf("%d%d%d", &man[i].a, &man[i].b, &man[i].c);  
  27.     for (int i = 1; i <= 2*n; ++i)  
  28.     {  
  29.         p[i] = i;  
  30.     }  
  31.     sort(man , man + m , cmp);  
  32.     for (int i = 0; i < m; ++i)  
  33.     {  
  34.         int x = find(man[i].a); int y = find(man[i].b);  
  35.         if(x == y)  
  36.         {  
  37.             cout << man[i].c << endl;  
  38.             return 0;  
  39.         }  
  40.         p[x] = find(man[i].b + n);  
  41.         p[y] = find(man[i].a + n);  
  42.     }  
  43.     printf("0\n");  
  44.     return 0;  
  45. }  

用并查集表示每个犯人之间的关系,在同一个集合中则说明两人在同一个监狱,反之则不在同一个监狱。

用类似于克鲁斯卡尔的方法,先将边排序,然后按照仇恨值的大小从大到小处理。对于每一组,先看两个人能不能加入不同的集合,
如果可以,将两人加入不同的集合,如果不可以,则输出该组仇恨值。
用补集来表示两个点不在一个集合中。如a和b'在同一个集合中,则a和b不在同一个集合中,不能把b直接加入另一个集合,因为会对后面的结果产生影响。
如,3和4不在同一个集合中,但是我们现在不知道究竟是3在第一个监狱还是4在第一个监狱,所以此时用3和4‘在同一个集合中来表示3和4不在同一个集合中。


原创粉丝点击