种类并查集

先来个最水的

poj 2524 Ubiquitous Religions

http://poj.org/problem?id=2524

【题意】给出n个大学生,其中有m对宗教信仰相同的学生,请你估算这n个学生中最多有多少种宗教信仰。

【思路】:  先设 答案为n  每合并一个集合减一就ok了

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <math.h>
 5 using namespace std;
 6 
 7 
 8 int n;
 9 int f[50002];
10 
11 void init()
12 {
13     for(int i=0;i<=n;i++)
14         f[i]=i;
15 
16 }
17 
18 
19 int find(int x)
20 {
21     return f[x]==x?x:f[x]=find(f[x]);
22 }
23 
24 int main()
25 {
26     int i,j,a,b,m,p=1;
27     while(scanf("%d%d",&n,&m))
28     {
29         if(n==0&&m==0)
30             break;
31         init();
32         int ans=n;
33         while(m--)
34         {
35             scanf("%d%d",&a,&b);
36             int aa,bb;
37             aa=find(a);
38             bb=find(b);
39             if(aa!=bb)
40             {
41                 f[aa]=bb;
42                 ans--;
43             }
44         }
45         printf("Case %d: %d\n",p++,ans);
46     }
47 
48     return 0;
49 }
View Code

 

poj 1611  The Suspects

http://poj.org/problem?id=1611

【题意】:N个组的学生,一个学生能同时加入不同的组,同一组的学生会同时感染病毒,现在0号的学生感染了病毒,问一共有多少个人感染病毒。

【思路】 并查集 若是两个 学生在的集合不同(根节点不同)就 合并  只要把一个根节点变成另一个根节点的father  同时新的根节点的 sum值 为两集合之和 就好了  在find x的时候 ,找根节点的同时更新sum[x]的值 (从离根最近的开始)

 1 #include<iostream>
 2 #include<stdio.h>
 3 using namespace std;
 4 
 5 int f[30002],sum[30002],n;
 6 
 7 void init()
 8 {
 9     for(int i=0;i<=n;i++)
10     {
11         sum[i]=1;f[i]=i;
12     }
13 }
14 
15 int find(int x)
16 {
17     if(f[x]==x)
18         return x;
19     f[x]=find(f[x]);
20     sum[x]=sum[f[x]];
21     return f[x];
22 }
23 
24 void lian(int tx,int ty)
25 {
26     f[ty]=tx;
27     sum[tx]+=sum[ty];
28 }
29 
30 int main()
31 {
32     int i,j,m,t,num,a,b;
33     while(~scanf("%d%d",&n,&m))
34     {
35         if(n==0&&m==0)
36             break;
37         init();
38         while(m--)
39         {
40             scanf("%d",&num);
41             scanf("%d",&a);
42             num--;
43             while(num--)
44             {
45                 scanf("%d",&b);
46                 int aa,bb;
47                 aa=find(a);
48                 bb=find(b);
49                 if(aa!=bb)
50                     lian(aa,bb);
51             }
52         }
53         printf("%d\n",sum[find(0)]);
54     }
55 }
View Code

 

poj 1703 

【题意】 有两个帮派,现在告诉你一些互相敌对的点对,然后让你判断某两个点之间的关系

【思路】只要 每个节点 多设一项 kind  表示和根节点的关系是相同还是不同 相同为0 不同为1 输入敌对的 关系时

 1 #include<iostream>
 2 #include<stdio.h>
 3 using namespace std;
 4 
 5 struct node{int kind; int fa;}no[100002];
 6 int n;
 7 
 8 void init()
 9 {
10     for(int i=0;i<=n;i++)
11     {
12         no[i].fa=i; no[i].kind=0;
13     }
14 }
15 
16 int find(int x)
17 {
18     if(no[x].fa==x)
19         return x;
20     int t;
21     t=find(no[x].fa)
22     //  这里执行完 x.fa 的fa和kind都求出来了  fa是 这个集合的根节点 kind是x.fa与根节点的关系 
23     //  而此时 x.fa 和x.kind 都未更新 下面就是 根据 x与x.fa的关系和 x.fa与根节点的关系 推出 x与根节点的关系
24     no[x].kind^=no[no[x].fa].kind;
25     return no[x].fa=t;
26 }
27 
28 int main()
29 {
30     int i,j,m,t,a,b;
31     char c[10];
32     scanf("%d",&t);
33     while(t--)
34     {
35 
36         scanf("%d%d",&n,&m);
37         init();
38         while(m--)
39         {
40             scanf("%s%d%d",c,&a,&b);
41             int aa,bb;
42             aa=find(a);
43             bb=find(b);
44             if(c[0]=='D')
45             {
46                 no[aa].fa=bb;
47                 no[aa].kind=no[a].kind^no[b].kind^1;//aa与 bb 的关系 有a与aa的关系 和b与bb的关系推出
48             }
49             else
50             {
51                 if(aa!=bb)
52                    printf("Not sure yet.\n");
53                 else
54                 {
55                     if(no[a].kind==no[b].kind)
56                         printf("In the same gang.\n");
57                     else
58                         printf("In different gangs.\n");
59                 }
60             }
61         }
62     }
63 }
View Code

 

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 using namespace std;
 5 
 6 int f[200020],sum[200020];
 7 
 8 void init(int n)
 9 {
10     memset(sum,0,sizeof(sum));
11     for(int i=0;i<=n;i++)
12         f[i]=i;
13 
14 }
15 
16 int find(int x)
17 {
18     if(f[x]==x)
19         return x;
20     int t=f[x];
21     f[x]=find(f[x]);
22     sum[x]+=sum[t];
23     return f[x];
24 }
25 
26 void lian(int x,int y,int a,int b,int v)
27 {
28     if(x > y)
29     {
30         f[y] = x;
31         sum[y] = sum[a]-v-sum[b];
32     }else
33     {
34         f[x] = y;
35         sum[x] = v + sum[b] - sum[a];
36     }
37 }
38 
39 int main()
40 {
41     int i,j,m,n,t,a,b,aa,bb,d;
42     while(scanf("%d%d",&n,&m)!=EOF)
43     {
44 
45         init(n);
46         int num=0;
47         while(m--)
48         {
49             scanf("%d%d%d",&a,&b,&d);
50             a--;
51             int aa,bb;
52             aa=find(a);
53             bb=find(b);
54             if(aa==bb&&sum[a]!=sum[b]+d)
55                 num++;
56             else if(aa!=bb)
57                 lian(aa,bb,a,b,d);
58 
59         }
60         printf("%d\n",num);
61     }
62     return 0;
63 }
View Code

 

poj 1988 Cube Stacking

http://poj.org/problem?id=1988

【题意】 有n个元素,开始每个元素自己一栈,有两种操作,将含有元素x的栈放在含有y的栈的顶端,合并为一个栈。第二种操作是询问含有x元素下面有多少个元素。

【思路】每个节点除了一项fa 还增加一项d 和一项 sum的表示的是这个节点下面的元素个数 也就是所求。 sum表示的是这个节点所在的集合的节点数

   根节点的d值总是0 。

 

 

 hdu 3038  How Many Answers Are Wrong  

http://acm.hdu.edu.cn/showproblem.php?pid=3038

【题意】 给定区间和  判定那个说法是错误的 

【思路】刚开始没做过种类并查集  做这个题目 半天没想明白。。

            sum[x]表示的是x到 x所在集合的根节点的区间和 

           下面应用别人的一段话:

          如果我们知道a到b之间的关系,a到c之间的关系,那么我们就可以知道a,b,c任意两个之间的关系,如果我们再知道了d和c之间的关系,那么我们就知道了a,b,c,d之间的关系,但是怎么表示这些关系呢??我们用的是并查集,顺便加一个每一个节点到根的距离,这样的话,任意两个点之间关系就可以通过求与根的距离求差得出,也就是说,如果输入的n,m在一个集合里,那么我们判断这两个的关系是否和已有的冲突,如果n,m不在一个集合里,那么我们就合并这两个集合,是的n,m这两个所在的两个集合之间的任意元素都有关系。

 

 合并的函数   

void lian(int x,int y,int a,int b,int v)
{
    if(x > y)
    {
        f[y] = x;
        sum[y] = sum[a]-v-sum[b];
    }else
    {
        f[x] = y;
        sum[x] = v + sum[b] - sum[a];
    }
}

这个怎么来的呢 我们始终选 较小根节点的最为 新的根节点

当y<x时     sum[x]-sum[b]+v+sum[a]=0    即   sum[x]=sum[b]-v-sum[a]

 

 

当x<y 时  sum[y]-sum[a]-v+sum[b]=0  即

sum[y] = sum[a]-v-sum[b];

 

 

 

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;

int f[200020],sum[200020];

void init(int n)
{
    memset(sum,0,sizeof(sum));
    for(int i=0;i<=n;i++)
        f[i]=i;

}

int find(int x)
{
    if(f[x]==x)
        return x;

    int t;
    t=find(f[x]);
    sum[x]+=sum[f[x]];

// find(f[x]) 已执行完 在这里 sum[f[x]]表示的是 f[x]到根节点的距离 而 find(x)还未执行完 sum[x]是 x到f[x]的距离
// x 到根节点的值 为sum[f[x]]+ sum[x]
return f[x]=t; } void lian(int x,int y,int a,int b,int v) { if(x > y) { f[x] = y; sum[x] = sum[b] - sum[a]-v; }else { f[y] = x; sum[y] = sum[a]+v-sum[b]; } } int main() { int i,j,m,n,t,a,b,aa,bb,d; while(scanf("%d%d",&n,&m)!=EOF) { init(n); int num=0; while(m--) { scanf("%d%d%d",&a,&b,&d); a--; int aa,bb; aa=find(a); bb=find(b); if(aa==bb&&sum[b]!=sum[a]+d) num++; else if(aa!=bb) lian(aa,bb,a,b,d); } printf("%d\n",num); } return 0; }

 

 poj 2492 A Bug's Life

http://poj.org/problem?id=2492

【题意】 给出m对在一起的虫子 看里面有没有同性恋

  和1703 差不多

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <math.h>
 5 using namespace std;
 6 struct node{int fa,kind;}no[2002];
 7 
 8 void init(int n)
 9 {
10     for(int i=0;i<=n;i++)
11     {
12         no[i].fa=i;
13         no[i].kind=0;
14     }
15 }
16 
17 int find(int x)
18 {
19     if (no[x].fa==x)
20         return x;
21     int t;
22     t=find(no[x].fa);
23     no[x].kind^=no[no[x].fa].kind;
24     return no[x].fa=t;
25 
26 }
27 
28 int main()
29 {
30     int i,t,j,a,b,m,n,p=1;
31     scanf("%d",&t);
32     while(t--)
33     {
34        int flag=0;
35 
36        scanf("%d%d",&n,&m); init(n);
37        while(m--)
38        {
39            scanf("%d%d",&a,&b);
40            int aa,bb;
41            aa=find(a);
42            bb=find(b);
43            if(aa!=bb)
44            {
45                no[aa].fa=bb;
46                no[aa].kind=no[a].kind^no[b].kind^1;
47            }
48            else
49            {
50                if(no[a].kind==no[b].kind)
51                    flag=1;
52            }
53        }
54        printf("Scenario #%d:\n",p++);
55        if(flag)
56         printf("Suspicious bugs found!\n");
57        else printf("No suspicious bugs found!\n");
58        printf("\n");
59    }
60 
61     return 0;
62 }
View Code

 

 poj 1456 

 

【题意】买卖N件东西,每件东西都有个截止时间和价值,在截止时间之前买都可以,而每个单位时间只能买一件。问最大获利

【思路】 这个我还真的没想到要用并查集 。。 还是看了别人的博客  将物品按照价格从高到底的顺序排列,购买一个就在时间点上做一个标记,只要不冲突就可以购买  我们把连续的被占用的区间看成一个集合(子树),它的根结点为这个区间左边第一个未被占用的时间点。
先排序,然后每次判断Find(b[i])是否大于0,大于0说明左边还有未被占用的空间,则占用它,然后合并(b[i], Find(b[i]) – 1)即可

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <math.h>
 5 #include<algorithm>
 6 using namespace std;
 7 struct node{int d;int num;}no[10002];
 8 int f[10002];
 9 int cmp(node a,node b)
10 {
11     return a.num>b.num;
12 }
13 
14 int find(int x)
15 {
16     return f[x]==x?x:f[x]=find(f[x]);
17 }
18 int main()
19 {
20     int i,j,n,m,t;
21     while(scanf("%d",&n)!=EOF)
22     {
23         for(i=0;i<n;i++)
24             scanf("%d%d",&no[i].num,&no[i].d);
25         sort(no,no+n,cmp);
26         for(i=0;i<10005;i++)
27             f[i]=i;
28             int ans=0;
29         for(i=0;i<n;i++)
30         {
31             int  t=find(no[i].d);
32             if(t>0)
33             {
34                 ans+=no[i].num;
35                 f[t]=t-1;
36             }
37         }
38         printf("%d\n",ans);
39     }
40 }

 

 

posted @ 2014-02-07 11:33  galaxy77  阅读(435)  评论(0)    收藏  举报