F - 娜娜梦游仙境系列——多民族王国
Problem Description
娜娜好不容易才回忆起自己是娜娜而不是什么Alice,也回忆起了自己要继续探索这个世界的目标,便偷偷溜出皇宫。娜娜发现这个王国有很多个民族组成,每个民族都有自己的方言,更要命的是这些方面差别还很远,这就导致这个王国的人民交流十分困难。娜娜仔细观察并记录了好久,发现总共有m种不同的语言。
突然娜娜发现前面有一群天才在讨论问题,但是奈何语言问题,导致这群人交流非常吃力。不过幸亏的是,这群天才都有一个特殊的能力,只要消耗一个单位的能量即可完全领悟一门新的语言(妈妈再也不用担心我的四六级托福雅思GRE!)。于是娜娜久违的的好奇心又开始冒泡了,娜娜希望你告诉她,如果知道了每个人会的语言,是否能让这群天才两两直接或者间接的交流呢?所谓间接得交流是指经过若干个人的翻译使两个人得到相互表达的信息。如果不能,至少需要多少能量才能实现呢?
Input
多组数据,首先是一个正整数t(t<=20)
对于每组数据,首先是两个整数n,m(2<=n<=100,1<=m<=100),分别代表人数以及语言的种类数,语言的编号从1~m。
接下来是n行,每行对这个人进行描述
首先是一个整数k,表示这个人已经会k门语言,接下来是k个整数,分别是这个人掌握的语言编号。(0<=k<=m)
Output
Sample Input
2 2 2 1 2 0 5 5 1 2 2 2 3 2 3 4 2 4 5 1 5
Sample Output
1 0
Hint
样例1中其中第一个人会第二种语言,而第二个人不会任何语言,所以只需要第二个人也学会第二种语言即可交流,所以能量数为1
样例2中有5个人,而且这5个人已经可以相互直接或间接进行交流: 1-2-3-4-5,正好构成一条链。因此不需要继续学习新的语言,能量数为0。
题意:有n个人,有相同语言的人连一条边,问还需要添加多少条边才能使图完全连通。
解法:可以用并查集来维护图的连通性,或者直接dfs或bfs来计算连通块数。若只有一个连通块答案为0(因为所有人都能无障碍间接或直接交流),每多一个连通块就要增加一条边,所以答案是连通块数-1,但是有一种特殊的情况就是所有人都不会语言,那么这种情况需要特判,答案为人数(即每个人都学同一种语言)。
note:秩其实还没搞懂具体怎么用的。。。
1 #include <stdio.h> 2 #include <string.h> 3 #define MAXSIZE 205 4 5 int uset[MAXSIZE],rank[MAXSIZE]; 6 7 void makeSet(int size) //为每个数创建集合,指向自己,rank保存秩 8 { 9 int i; 10 for(i = 0;i < size;i++) 11 { 12 uset[i] = i; 13 rank[i] = 0; 14 } 15 } 16 17 int find(int x) { //路径压缩,把所有元素指向根 18 19 if (x != uset[x]) 20 uset[x] = find(uset[x]); 21 return uset[x]; 22 23 } 24 25 int unionSet(int x, int y) { //联合两个集合,返回是否两个不同集合 26 27 if ((x = find(x)) == (y = find(y))) 28 return 0; 29 if (rank[x] > rank[y]) 30 uset[y] = x; 31 else 32 { 33 uset[x] = y; 34 if (rank[x] == rank[y]) 35 rank[y]++; 36 } 37 return 1; 38 } 39 40 int main() 41 { 42 int t,i,j,s,k,mark,ski,n,m,p,q,l,sum; 43 while(scanf("%d",&t)==1) 44 { 45 while(t--) 46 { 47 scanf("%d %d",&n,&m); 48 s=sum=0; 49 makeSet(MAXSIZE); 50 for(i=1;i<=n;i++) 51 { 52 scanf("%d",&ski); 53 if(ski==0) 54 sum++; 55 for(j=0;j<ski;j++) 56 { 57 scanf("%d",&k); 58 p=unionSet(k+100,i); //把语言种类+100放到人物i中 59 if(p==0) //说明该语言别的人会 60 { 61 q=find(k+100); 62 unionSet(i,q); //把该人物和能交流的另一个人放入一个集合 63 //q=q<i?q:i; 64 //mark=1; 65 } 66 } 67 } 68 if(sum==n) //所有人会的语言为0 69 printf("%d\n",n); 70 else 71 { 72 for(i=1;i<=n;i++) 73 { 74 if(uset[i]==i) //根指向自身,所以满足的话就是一个集合,否则是属于别的集合的,即是相关的。 75 s++; 76 } 77 printf("%d\n",s-1); //n个集合,只要n-1个边就可以了 78 } 79 } 80 } 81 return 0; 82 }