宗教信仰(并查集)

宗教信仰
总时间限制: 5000ms 内存限制: 65536kB
描述
世界上有许多宗教,你感兴趣的是你学校里的同学信仰多少种宗教。
你的学校有n名学生(0 < n <= 50000),你不太可能询问每个人的宗教信仰,因为他们不太愿意透露。但是当你同时找到2名学生,他们却愿意告诉你他们是否信仰同一宗教,你可以通过很多这样的询问估算学校里的宗教数目的上限。你可以认为每名学生只会信仰最多一种宗教。
输入
输入包括多组数据。
每组数据的第一行包括n和m,0 <= m <= n(n-1)/2,其后m行每行包括两个数字i和j,表示学生i和学生j信仰同一宗教,学生被标号为1至n。输入以一行 n = m = 0 作为结束。
输出
对于每组数据,先输出它的编号(从1开始),接着输出学生信仰的不同宗教的数目上限。
样例输入

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

样例输出

Case 1: 1
Case 2: 7

思路点拔:本题是经典的并查集,首先,每种宗教可以看成一个独立的集合,然后,两个大学生信仰同一种宗教,就说明这两个元素是等价类,就合并这两个元素,最后输出集合的个数就是最终的答案,注意:本题是无限输入,所以每次数组都要清0.

#include<cstdio>
#include<cstring>
const int maxn=50005;
int fa[maxn],ans,m,n,a,b,tot=1;
bool flag[maxn];
void makeSet(int n)
 //初始化,最开始,每种宗教都自成一个集合
{
    for(int i=1;i<=n;i++)
    {
        fa[i]=i;
    }   
}
int findSet(int x) 
//寻找元素属于哪个集合(寻找根节点)
{
    while(fa[x]!=x)
    {
        x=fa[x];
    }
    return x;
}
void unionSet(int a1,int b1)
//合并,将需要合并的集合接在对应的根节点下面
{
    int x=findSet(a1),y=findSet(b1);
    fa[x]=y;
}
int main()
{
    while(scanf("%d %d",&n,&m)&&n)//无限输入
    { 
        memset(flag,0,sizeof(flag)); //清空数组
        makeSet(n); //初始化
        ans=0;
        for(int i=1;i<=m;i++)
        {
            scanf("%d %d",&a,&b); 
//输入那两个大学生的宗教信仰相同,就将他们两个合并在一个集合里
            unionSet(a,b);
        }
        for(int i=1;i<=n;i++)
        {
            if(flag[findSet(i)]==0)
             //如果这个集合还没有算
            {
                ans++; //集合数加1
                flag[findSet(i)]=1;
                //将这个根节点对应的元素赋值为1
            }
        }
        printf("Case %d: %d\n",tot,ans); 
        //输出每组数据对应的方案数
        tot++; 
    } 
    return 0;   
} 

猜你喜欢

转载自blog.csdn.net/qq_42995099/article/details/82052615