并查集

  并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。

  这样说可能不是很易懂,来看一道例题:题目链接 poj2524

  当今世界上有许多不同的宗教,要了解它们是很困难的。你想知道你们学校的学生信仰多少种不同的宗教。你知道你们学校有n个学生(0 <n(= 50000))。你不可能问每个学生的宗教信仰。此外,许多学生对表达他们的信仰感到不自在。避免这些问题的一种方法是问m (0 <=m (= n(n-1)/2)对学生,问他们是否信仰相同的宗教(例如,他们可能知道他们是否参加同一个教堂)。从这些数据中,你可能不知道每个人都信仰什么,但你可以了解校园里可能代表多少种不同宗教的上限。你可能认为每个学生最多只信奉一种宗教。

  输入输入由许多情况组成。下面的m行分别由两个整数i和j组成,指定学生i和j信仰同一宗教。学生们编号为1至n.输入端由n=m=0的行指定。

  输出对于每个测试用例,在一行中打印案例编号(从1开始),然后是该大学学生信仰的不同宗教的最大数量。

Sample Input

10 9
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
10 4
2 3
4 5
4 8
5 8
0 0

Sample Output

Case 1: 1
Case 2: 7

  以第一个样例为例:首先我们假设九个学生都是独立的集合{1},{2},...,{9}。第一个关系表示1号和2号学生信仰一个宗教,所以1所在的集合应该和2所在的集合合并。所以集合就变成了{1,2},{3},...,{9}。第二个关系表示1和3信仰一个宗教,所以合并1和3所在的集合{1,2,3},{4},...,{9}。依次类推,最后集合数目为1,所以全部学生只信仰一个宗教。

  怎么实现呢?引如disjoint_set[ ]数组,首先让disjoint_set[i]=i;如果1和2要合并成为一个集合,让disjoint_set[1]=disjoint_set[2]即可;

  那么如何表示不同的学生信仰一个宗教,也就是在一个集合中呢?比如集合{1,2,3}如果disjoint_set[1]==disjoint_set[2]==disjoint_set[3]则表示1,2,3这三个元素在一个集合中。

  代码实现:

复制代码
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <vector>
 4 
 5 using namespace std;
 6 int disjoint_set[50010];
 7 int a[50010];
 8 int findset(int x){
 9     return disjoint_set[x]!=x?disjoint_set[x]=findset(disjoint_set[x]):x;
10 }
11 
12 int readint(){
13     int x;scanf("%d",&x);return x;
14 }
15 
16 int main(){
17     int n,m;
18     int ss=0;
19     while(~scanf("%d%d",&n,&m)){
20             ss++;
21         if(n==0&&m==0)break;
22         for(int i=1;i<=n;i++){
23             disjoint_set[i]=i;
24             a[i]=0;
25         }
26         for(int i=0,v,c;i<m;i++){
27             scanf("%d%d",&v,&c);
28             int x=findset(v);
29             int y=findset(c);
30             if(x!=y){
31                 disjoint_set[x]=y;
32             }
33         }
34         for(int i=1;i<=n;i++){
35             a[findset(i)]=1;
36         }
37         int sum=0;
38         for(int i=1;i<=n;i++){
39             if(a[i]==1)sum++;
40         }
41         printf("Case %d: ",ss);
42         printf("%d\n",sum);
43     }
44     return 0;
45 }
复制代码

 

posted @ 2019-01-17 15:56  DWVictor  阅读(262)  评论(0编辑  收藏  举报