GDUT 专题三 C - The Suspects(并查集 / 图的遍历 两种解法) 2022

题面:2019冠状病毒病(英语:Coronavirus disease 2019,缩写:COVID-19 ),是一种由严重急性呼吸系统综合症冠状病毒2型(缩写:SARS-CoV-2)引发的传染病。此病在全球各国大规模爆发并急速扩散,成为人类历史上致死人数最多的流行病之一。 很显然,目前最好的办法就是将所有可能的患者都隔离起来。 现在某高校正在排查可能的患者,这个高校中有多个社团,每个社团经常进行内部交流,一名学生可能会加入多个社团。学校认为一旦某个社团里出现一名可疑患者,这整个社团的学生都被视为是可能的患者。 现在请你帮忙找到这所学校的所有可能的患者。

Input
输入文件包含多组数据。
对于每组测试数据:
第一行为两个整数n和m, 其中n是学生的数量, m是社团的数量。0 < n <= 30000,0 <= m <= 500。
接下来m行,每一行有一个整数k,代表社团中学生的数量。之后,有k个整数代表这个社团里每个学生的编号(在0到n-1之间)。
n = m = 0表示输入结束,不需要处理。

Output
对于每组测试数据, 输出可能的患者数目。
Sample Input
100 4
2 1 2
6 11 13 50 12 14
2 0 1
2 99 2
200 2
1 5
6 5 6 7 8 9 10
1 0
0 0
Sample Output
4
1
1

思路:我们可以将题面翻译为:
1)求第0号患者所在集合的人数
2)求从第0号点(患者看成一个点)出发,求可以到达的点的数量,或者说与第0号点联通的点的数量
明显思路一应该用并查集来实现(即与第0号患者的父结点相同的点),思路二则是一般的连接图的遍历
具体看代码实现

AC代码
1)

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
const int N = 3e4;
int fa[N + 5], a[N + 5];
int n, m, cnt, ans;

int query(int x)
{
	if(x != fa[x])	fa[x] = query(fa[x]);
	return fa[x];
}

int main()
{
	while(1)
	{
		ans = 0;
		scanf("%d%d", &n, &m);
		if(!n && !m)	break;
		for(int i = 0; i <= n; ++i)		fa[i] = i;
		for(int i = 1; i <= m; ++i)
		{
			scanf("%d", &cnt);
			if(!cnt)	continue;
			for(int i = 1; i <= cnt; ++i)
				scanf("%d", &a[i]);
			int r1 = query(a[1]);
			for(int i = 2; i <= cnt; ++i)
			{
				int r2 = query(a[i]);
				fa[r2] = r1;
			}
		}
		int r1 = query(fa[0]);
		for(int i = 0; i <= n; ++i)
		{
			if(query(fa[i]) == r1)
				++ans;
		}
		printf("%d\n", ans);
	}
	
	return 0;
}
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int N = 3e4, M = 5e2, NM = 10 * N;
int a[N + 1];
int n, m, cnt, ans, tot;
int fir[NM], nex[NM], ver[NM], que[2 * N + 5];
bool vis[N + 5];

void add(int x, int y)
{
	nex[++tot] = fir[x]; fir[x] = tot; ver[tot] = y;//连接表存图
	//printf("nex[%d] = %d, fir[%d] : %d, ver : %d\n", tot, nex[tot], x, fir[x], ver[tot]);
	nex[++tot] = fir[y]; fir[y] = tot; ver[tot] = x;
	//printf("nex[%d] = %d, fir[%d] : %d, ver : %d\n", tot, nex[tot], y, fir[y], ver[tot]);
}

int main()
{
	while(1)
	{
		scanf("%d%d", &n, &m);
		if(!n && !m)	break;
		memset(fir, 0, sizeof(fir)); memset(nex, 0, sizeof(nex));
		memset(vis, 0, sizeof(vis)); 
		tot = 0;
		for(int k = 1; k <= m; ++k)
		{
			scanf("%d", &cnt);
			for(int i = 1; i <= cnt; ++i)
				scanf("%d", &a[i]);
			for(int i = 1; i < cnt; ++i)
			{
				add(a[i], a[i + 1]);	
			}
		}		
		int head = -1, tail = 0;
		que[tail] = 0; ans = 0; vis[0] = 1;
		while(head != tail)
		{
			++ans;
			head = (head + 1) % (2 * N);
			int u = que[head];
			for(int i = fir[u], v; v = ver[i], i; i = nex[i])
			{
				if(!vis[v])
				{
					tail = (tail + 1) % (2 * N);
					que[tail] = v; vis[v] = 1;
				}
			}
		}	
		printf("%d\n", ans);
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值