输入:
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。
扫描二维码关注公众号,回复:
11599541 查看本文章
思路2:并查集
todo