【图】【并查集】无所不在的宗教

问题 B(1354): 无所不在的宗教

时间限制: 1 Sec   内存限制: 64 MB

题目描述

世界上许多不同的宗教,你很难把它们全都记住。你有兴趣找出学生们到底信仰多少种不同的宗教。 已知学校共有N名学生(0 < N ≤ 50000),编号从1..N。你去询问每一个学生的宗教信仰是不可行的,而且,很多学生不愿意透露自己的信仰。一种避免这个问题的方法是询问M对学生(0 ≤ M ≤ N(N-1)/2)是否信仰同一种宗教。假定每个学生只信仰一种宗教,而且每个学生都要信仰一种宗教。

输入

输入包括若干个组数据,每组数据格式为: 第1行:2个空格分开的整数N和M 接下来M行,每行2个整数i和j,表示第i号学生与第j号学生信仰相同 输入以2个空格分开的0结束

输出

对每组输入数据,输出最多有几种不同的宗教 输出格式见样例

样例输入

Copy (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

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

提示

数据量巨大,C++程序推荐用 scanf

#----------------------------------------------------------------------------------------------#

写这道题并不是想说什么思路,主要是想记录一个惊天地泣鬼神的东西(这一点也不夸张……)

很多时候并查集需要找集合的个数(比如这道题),所以,我们应该怎么办呢?

我的第一次方法是:

找每一个结点的根,用一个bool数组记录,有一个新的就sum++:

for(int i=1;i<=n;i++)
            if(!ans[father[i]])
            {
                ans[father[i]]=1;
                sum++;
            }

于是就有了严重的错误(还很耗时……对于这种在其他OJ上5秒限制而这里1秒限制来说就更恶心得要死了)……


所以,我发现了一个方法(尽管很容易想到,然而我没有):

直接数根结点个数!

我一下子就觉悟了……

反正我的根结点标记是-1,就这样即可:

for(int i=1;i<=n;i++)
			if(father[i]==-1)
				sum++;

我用了路径压缩,所以不用再找root。


代码:

#include<cstdio>
#include<cstring>
int father[50005];
int n,m,k;
int root(int x)
{
	if(father[x]==-1)
		return x;//找到根
	father[x]=root(father[x]);//找的时候路径压缩,路径压缩是什么?简单说就是使一个集合的除根结点以外所有点的father都变为根结点
	return father[x];
}
int main()
{
	while(1)
	{
		scanf("%d%d",&n,&m);
		if(!n&&!m) return 0;
		memset(father,-1,sizeof(father));//默认每个数都是根结点
		for(int i=1;i<=m;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			int rx=root(x),ry=root(y);
			if(rx!=ry)//两个数不在一个集合
				father[ry]=rx;//就合并
		}
		int sum=0;
		for(int i=1;i<=n;i++)
			if(father[i]==-1)
				sum++;//刚刚说的方法
		printf("Case %d: %d\n",++k,sum);
	}
}

                                                                                                                                        By WZY

#----------------------------------------------------------------------------------------------#

转载于:https://www.cnblogs.com/LinqiongTaoist/p/7203739.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值