严重急性呼吸系统综合症( 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
题意:就是说一组人中有一个得病,这组所有的孩子都得病了(孩子编号为0 - n-1),假定刚开始的时候是第0个孩子得病,问最后得病的孩子有多少个;
思路:当然用并查集,可是怎样来做,很正常会将一组的孩子归并到一个树下,如果孩子有交叉,那就两个树根的值相加,合并在一个树下,最后找到第0个孩子所在组的树根下有多少孩子就可以了,注意两个分组合并的是按照高得合并。
注意:这题有点绕,既要记录每个树根的大小,还要记录每个孩子的等级,还要记录孩子的所在的分组。
AC代码:
#include <iostream>
#include <cstdio>
using namespace std;
int fu[30005];//每个孩子的父亲结点
int zi[30005];//每个孩子的等级
int ra[30005];//每个树根下孩子的数量
int found (int x){
return x = x == fu[x] ? x : found(fu[x]);//找到孩子的父亲结点
}
void bing(int x, int y){
x = found(x);
y = found(y);
if(x == y){
return;
}
if(zi[x] > zi[y]){//按照孩子的等级来合并,把等级低的合并到等级高的上
fu[y] = x;
ra[x] += ra[y];//等级高的孩子的所在的父亲结点的数量相加
}
else{
fu[x] = y;
if(zi[x] == zi[y]){//当两个孩子等级相等,那就把大的孩子的等级加1
zi[y]++;
}
ra[y] += ra[x];
}
}
int main(){
int a,b;
int n,m;
int nn;
while(~scanf("%d%d",&n,&m)){
if(n == 0 && m == 0){
break;
}
if(n == 1){
printf("1\n");
continue;
}
for(int i = 0; i < n;i++){
fu[i] = i;
zi[i] = 0;//初始化,每个孩子的等级相等都是0,每个孩子都是一个根,数量为1.
ra[i] = 1;
}
while(m--){
scanf("%d",&nn);
scanf("%d",&a);
for(int i = 1;i < nn;++i){
scanf("%d",&b);
bing(a,b);
a = b;
}
}
printf("%d\n",ra[found(0)]);//找到编号为0的孩子所在的树根,输出当前根下的数量
}
return 0;
}
总结:这些题的思路还是好难想,我要继续加油加油。。。。。