题目链接
题目描述
严重急性呼吸系统综合症( SARS), 一种原因不明的非典型性肺炎,从2003年3月中旬开始被认为是全球威胁。为了减少传播给别人的机会, 最好的策略是隔离可能的患者。
在Not-Spreading-Your-Sickness大学( NSYSU), 有许多学生团体。同一组的学生经常彼此相通,一个学生可以同时加入几个小组。为了防止非典的传播,NSYSU收集了所有学生团体的成员名单。他们的标准操作程序(SOP)如下:
一旦一组中有一个可能的患者, 组内的所有成员就都是可能的患者。
然而,他们发现当一个学生被确认为可能的患者后不容易识别所有可能的患者。你的工作是编写一个程序, 发现所有可能的患者。
输入格式
输入文件包含多组数据。
对于每组测试数据:
第一行为两个整数n和m, 其中n是学生的数量, m是团体的数量。0 < n <= 30000,0 <= m <= 500。
每个学生编号是一个0到n-1之间的整数,一开始只有0号学生被视为可能的患者。
紧随其后的是团体的成员列表,每组一行。
每一行有一个整数k,代表成员数量。之后,有k个整数代表这个群体的学生。一行中的所有整数由至少一个空格隔开。
n = m = 0表示输入结束,不需要处理。
输出格式
对于每组测试数据, 输出一行可能的患者。
输入输出样例
输入 #1
100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2
200 2
1 5
5 1 2 3 4 5
1 0
0 0
输出 #1
4
1
1
题目分析
由题知:有 n 个学生,编号为 0 到 n-1,初始时 0 号学生为患者。最终的患者数即为 0 号学生所在团队的学生数目。
因为涉及到了集合和数目,我们会自然想到运用带权并查集来解决此问题。
首先开一个记录数目的数组(记为num[maxn],全部初始为 1 ,代表团队中只有自己一个人。接下来每次合并时,新的团队中的数目等于两个合并团队中的数目相加(num[x] += num[y])。最终的患者数目为num[Find(0)]。
AC代码:
#include <stdio.h>
const int maxn = 3e4+10;
int f[maxn], h[maxn], num[maxn], n, m, k, x, y;
void Init() {
for(int i=0; i<n; i++) {
f[i] = i;
h[i] = 0;
num[i] = 1;
}
}
int Find(int i) {
return f[i]==i ? f[i] : f[i]=Find(f[i]);
}
void merge(int a, int b) {
int fa = Find(a);
int fb = Find(b);
if(fa != fb) {
if(h[fa] < h[fb]) {
f[fa] = fb; //把 a的根指向 b的根
num[fb] += num[fa]; //把 a所在团队的数目加到 b所在团队中
} else {
f[fb] = fa; //把 b的根指向 a的根
num[fa] += num[fb]; //把 b所在团队的数目加到 a所在团队中
if(h[fa] == h[fb]) h[fa]++;
}
}
}
int main(void) {
while(scanf("%d %d", &n, &m) != EOF) {
if(n==0 && m==0) return 0; //直到 n与 m同时为 0时,结束
Init(); //初始化
while(m--) {
scanf("%d", &k);
scanf("%d", &x); //先输入第一个学生编号
for(int i=2; i<=k; i++) {
scanf("%d", &y); //输入接下来的学生编号
merge(x, y); //使后来的学生都进入第一个学生所在的团队
}
}
printf("%d\n", num[Find(0)]);
//找到编号为 0的初始学生的根节点
//其对应的数目即为所有患者的数目
}
}