输入:
N M
3 5 6 1
0 1
2 4 7
3 8 9
输入的N表示一共有几个人(话说这个参数我好想没用到),M表示一共有几个小团体,下面的M行就表示每个小团体里面的人的编号,比如3 5 6 1就表示第0个小团体里面有编号3 5 6 1的人。每个人可以分属于几个不同的团体或者不属于任何团体
一开始会向编号为0的人传达一个消息,然后0会把这个消息传递到所在组里面的每个人,每个人又会传递给他的组里面的每个人,求最后一共有多少人知道这个消息。
思路1:最朴素的思想,我们建立一个idset(一个hashset)保存所有知道消息的人的编号,然后建立一个rowsSet存放已经知道这个消息的小团体(就是行号,防止后面再遍历搜索这一行),然后建立一个队列,所有会知道这个消息的人都会进入这个队列。首先0知道了那么这一行的所有人都会知道,都放入队列。然后不断把队列里面的元素取出来,然后取出不在rowsSet里面的行的元素,都加入这个队列里面。
之所以要这个队列,是因为我们不确定要遍历到什么时候才算结束,因为如果一开始加入 0 1,然后自然要遍历第0行(因为第0行有1),但是第0行的3又出现在第3行,所以我们又需要去遍历第三行的所有元素,如果第三行里面还有个比如x,它又出现在第五行,我们又要去遍历第5行,因此用一个队列把所有需要查找的元素都放进去,最后遍历结束的标志就是队列为空。
public static int res=0;
//start表示0所在的行
public static HashSet<Integer> idset=new HashSet<>();//存放结果编号
public static void process(List<List<Integer>> all,int start){
int number=all.get(start).size();//这一行的数量
Queue<Integer> queue=new LinkedList<>();
Set<Integer> rowsSet=new HashSet<>();//存放已有的行编号
rowsSet.add(start);
for(int i=0;i<number;i++){
queue.add(all.get(start).get(i));
}
while(!queue.isEmpty()){
int bianhao=queue.poll();//我们需要找的这个编号
idset.add(bianhao);
for(int j=0;j<all.size();j++){//遍历所有行
if(j!=start){
if(all.get(j).contains(bianhao)&&!rowsSet.contains(j)){//这一行如果有我们要的编号,并且之前没有遍历
rowsSet.add(j);
for(int m=0;m<all.get(j).size();m++){//遍历这一行的所有数字
int tmp=all.get(j).get(m);//找出第j行的第m个
if(!idset.contains(tmp)){
idset.add(tmp);
queue.add(tmp);
}
}
}
}
}
}
res=idset.size();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc=new Scanner(System.in);
while(sc.hasNext()){
int n=sc.nextInt();
int m=sc.nextInt();//小团体的个数
sc.nextLine();
List<List<Integer>> all=new ArrayList<>();//小团体都放入all里面
int start=-1;
for(int i=0;i<m;i++){
List<Integer> tmp=new ArrayList<>();
int count=sc.nextInt();
for(int j=0;j<count;j++){
int num=sc.nextInt();
tmp.add(num);
if(num==0)
start=i;//把0所在的行保存
}
all.add(tmp);
sc.nextLine();
}
process(all,start);
System.out.println(res);
}
}
上述代码确实略微繁琐,但是却能够100%AC。
思路2:并查集
public static class UnionFindSet{
public HashMap<Integer,Integer> fathermap;
public HashMap<Integer,Integer> sizemap;
public UnionFindSet(int n){//初始化
fathermap=new HashMap<>();
sizemap=new HashMap<>();
for(int i=0;i<n;i++){
fathermap.put(i,i);
sizemap.put(i,1);
}
}
public void union(int m,int n){
int father1=findFather(m);
int father2=findFather(n);
if(father1!=father2){
int size1=sizemap.get(father1);
int size2=sizemap.get(father2);
if(size1<size2){
fathermap.put(father1, father2);
sizemap.put(father2, size1+size2);
}else{
fathermap.put(father2, father1);
sizemap.put(father1, size1+size2);
}
}
}
//递归找到父节点,同时修改路过的每个节点的父节点
public int findFather(int num){
int father=fathermap.get(num);
if(father!=num){
father=findFather(father);
}
fathermap.put(num,father);
return father;
}
}
public static int process2(List<List<Integer>> all,UnionFindSet unionset){
for(List<Integer> list:all){
int start=list.get(0);
for(Integer a:list){
unionset.union(start, a);
}
}
int father=unionset.findFather(0);
return unionset.sizemap.get(father);
}