POJ1611-The Suspects(并查集结构的基本使用)
题目链接
题意
就是告诉你0号同学被感染了,他还参加了一些社团,给出一些社团以及里面的人,问总共多少人感染。输入给出n表示人数(标号为0~n-1),m表示社团数目,接下来m行每行第一个数k ,表示该社团有k人,然后是k个人的编号。要你输出有多少个人感染了病毒。
解析
把每个社团加入到各自的并查集中,然后合并,最后看哪些和0号同学在同一个集合中,使用一个变量记录和0号同学在同一个集合中的人数即可。
注意
并查集有两个优化
- 第一个优化: 在findHead(查找某个元素的根的时候)递归查找完毕之后,将沿途的所有结点的父亲都指向根节点;
- 第二个优化:使用一个sizeMap来记录每个集合的元素个数(高度),在合并的时候,将小集合的根直接挂到大集合的根即可;
import java.io.BufferedInputStream;
import java.util.HashMap;
import java.util.Scanner;
public class POJ1611_TheSuspects {//提交时改成main
private static class UnionSet{
public HashMap<Integer,Integer>faMap;
public HashMap<Integer,Integer>sizeMap;
public UnionSet() {
faMap = new HashMap<Integer,Integer>();
sizeMap = new HashMap<Integer,Integer>();
}
public Integer findHead(Integer v){
Integer fa = faMap.get(v);
if(fa != v){
fa = findHead(fa);
}
faMap.put(v,fa); //找的过程中,顺便优化,从根到这个结点路上的结点全部挂在根下面
return fa;
}
public boolean isSameSet(Integer a,Integer b){
return findHead(b) == findHead(a);
}
public void union(Integer a,Integer b){ //优化是小的挂在大的下面
if(a == null || b == null)return;
Integer aF = findHead(a);
Integer bF = findHead(b);
if(aF == bF) return;
int aSize = sizeMap.get(a);
int bSize = sizeMap.get(b);
if(aSize >= bSize){
faMap.put(bF,aF);//bF的父亲是aF
sizeMap.put(aF,aSize + bSize);
}else {
faMap.put(aF,bF);
sizeMap.put(bF,aSize + bSize);
}
}
}
public static void main(String[] args) {
Scanner cin = new Scanner(new BufferedInputStream(System.in));
while(cin.hasNext()){
int n = cin.nextInt();
int m = cin.nextInt();
if( n == 0 && m == 0)break;
UnionSet unionSet = new UnionSet();
for(int i = 0; i < n; i++) unionSet.faMap.put(i,i); //自己成一个集合
for(int i = 0; i < n; i++) unionSet.sizeMap.put(i,1); //每个集合的大小为1
for(int i = 0; i < m; i++){
int k = cin.nextInt();
int root = cin.nextInt();
for(int j = 0; j < k-1; j++){ //k-1个
int x = cin.nextInt();
unionSet.union(root,x); //这个集合的根
}
}
int sum = 1; //0号已经被感染
for(int i = 1; i < n; i++){
if(unionSet.isSameSet(0,i)){
sum++;
}
}
System.out.println(sum);
}
}
}