HDU - The Suspects
Description
严重急性呼吸系统综合症( SARS), 一种原因不明的非典型性肺炎,从2003年3月中旬开始被认为是全球威胁。为了减少传播给别人的机会, 最好的策略是隔离可能的患者。
在Not-Spreading-Your-Sickness大学( NSYSU), 有许多学生团体。同一组的学生经常彼此相通,一个学生可以同时加入几个小组。为了防止非典的传播,NSYSU收集了所有学生团体的成员名单。他们的标准操作程序(SOP)如下:
一旦一组中有一个可能的患者, 组内的所有成员就都是可能的患者。
然而,他们发现当一个学生被确认为可能的患者后不容易识别所有可能的患者。你的工作是编写一个程序, 发现所有可能的患者。
Input
输入文件包含多组数据。
对于每组测试数据:
第一行为两个整数n和m, 其中n是学生的数量, m是团体的数量。0 < n <= 30000,0 <= m <= 500。
每个学生编号是一个0到n-1之间的整数,一开始只有0号学生被视为可能的患者。
紧随其后的是团体的成员列表,每组一行。
每一行有一个整数k,代表成员数量。之后,有k个整数代表这个群体的学生。一行中的所有整数由至少一个空格隔开。
n = m = 0表示输入结束,不需要处理。
Output
对于每组测试数据, 输出一行可能的患者。
Sample Input
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
Sample Output
4 1 1
这道题算是我的第一道开始接触并查集类型的题目,我觉得很有纪念意义,虽然搞懂并查集之后比较容易,不过我还是要贴一下代码,毕竟纪念嘛。
一开始我面对这倒题目也是一脸懵比的,因为还不清楚到底怎么写出并查集来,不过在经过菊花哥打通任督二脉之后连下六题倒是很爽,嘻嘻
至于并查集是什么不懂先去百度一下吧,我这里主要还是根据我自己的理解写一点笔记
并查集中用数组来表示集合,本节点储存的是父节点的信息,在查找的时候类似于链表,一路向上查找直到代表一个集合的那个节点,所谓路径压缩,
就是在查找那个集合代表的时候中途也把路过的节点全部指向集合标志节点,要理解这一个递归就好办事了
int FindRoot(int root) {
return road[root] = root ? root : (road[root] = FindRoot(root));
}
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
using namespace std;
const int MX = 33333;
int node[MX];
int num[MX];
int in[MX];
int n, m;
void ini() {
for (int i = 0; i < n; i++) {//初始化的时候,因为每一个节点的父节点都是自己本身(规定这样的,嗯,记住就好,以后用多的时候自然就懂了)
num[i] = 1;//这个数组用来记录集合代表节点所包含的元素,当然也包括自己本身,所以初始化为1
node[i] = i;
}
}
int FindRoot(int k) {
return node[k] == k ? k : (node[k] = FindRoot(node[k]));
}
int main() {
//freopen("input.txt", "r", stdin);
while (scanf("%d %d", &n, &m), n || m) {
ini();
while (m--) {
int t;
scanf("%d", &t);
for (int i = 0; i < t; i++) {
scanf("%d", &in[i]);
if (i == 0) continue;
else {
int root1 = FindRoot(in[i - 1]);
int root2 = FindRoot(in[i]);
if (root1 != root2) {
node[root2] = root1;
num[root1] += num[root2];//合并的时候别忘了把元素个数算上
}
}
}
}
printf("%d\n", num[FindRoot(0)]);//这里要特别注意,因为0的跟节点可能不是0,所以不能直接输num【0】,要更新一遍
}
return 0;
}