POJ1611-The Suspects(并查集结构的基本使用)

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);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/zxzxzx0119/article/details/81536185