程序设计思维与实践 Week6 作业 B - 戴好口罩!

题目

新型冠状病毒肺炎(Corona Virus Disease 2019,COVID-19),简称“新冠肺炎”,是指2019新型冠状病毒感染导致的肺炎。
如果一个感染者走入一个群体,那么这个群体需要被隔离!小A同学被确诊为新冠感染,并且没有戴口罩!!!!!!危!!!时间紧迫!!!!
需要尽快找到所有和小A同学直接或者间接接触过的同学,将他们隔离,防止更大范围的扩散。
众所周知,学生的交际可能是分小团体的,一位学生可能同时参与多个小团体内。请你编写程序解决!戴口罩!!

Input
多组数据,对于每组测试数据:
第一行为两个整数n和m(n = m = 0表示输入结束,不需要处理),n是学生的数量,m是学生群体的数量。0 < n <= 3e4 , 0 <= m <= 5e2
学生编号为0~n-1
小A编号为0
随后,m行,每行有一个整数num即小团体人员数量。随后有num个整数代表这个小团体的学生。

Output
输出要隔离的人数,每组数据的答案输出占一行

思路

这一题很显然就是并查集的应用。这里我使用了结构体数组来实现树形并查集,结构体有元素域和父节点域,同时还创建了一个用于存储每个集合成员个数的数组。每棵树将其根作为该集合代表元素,若父节点域为-1则说明为该集合的根。一开始每个人都是一个单独的集合,根据输入不停的执行集合查、集合并操作,最后再查0号元素所在集合即可。若0号元素所在集合根元素为 a ,则在先前的成员个数数组中找 a 号元素即为最后的答案。这里有关一些细节我还要再多说几句,在集合查的操作中加入路径压缩的操作可大幅减少计算量,而且实现起来并不困难;这里我还利用之前的成员个数数组在集合并操作的时候实现小树依附大树,这样也可减少一定计算量,因为该操作缩短了树高;最后,即便我实现了以上功能,我提交的时候还是发现会超时,在仔细检查以后发现,原来是我没有判断当两个元素集合相同时的合并情况,当两个相同集合互相合并时会出现自己指向自己的情况,于是无限查询导致超时。

代码

#include <stdio.h>
struct set
{
	int num;
	int parent;
};
int count[30010];
set s[30010];
int find(int n)
{
	int p=n;
	if(s[n].parent!=-1)
	{
		while(s[p].parent!=-1)
			p=s[p].parent;
		s[n].parent=p;
	}
	return p;
}

void unite(int m,int n)
{
	int a=find(m);int b=find(n);
	if(a==b)
		return;
	if(count[a]<count[b])
	{
		int tmp;
		tmp=a;
		a=b;
		b=tmp;
	}
	s[b].parent=a;
	count[a]=count[a]+count[b];
}
int main()
{
	int m,n;
	while(1)
	{
		scanf("%d %d",&n,&m);
		if(n==0&&m==0)
			break;
		for(int i=0;i<n;i++)
			count[i]=1;
		for(int i=0;i<n;i++)
		{
			s[i].num=i;
			s[i].parent=-1;
		}
		for(int i=0;i<m;i++)
		{
			int k,f;
			scanf("%d %d",&k,&f);
			for(int j=0;j<k-1;j++)
			{
				int v;
				scanf("%d",&v);
				unite(f,v);
			}
		}
		int p=find(0);
		printf("%d\n",count[p]);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值