题目描述

Bytedance Efficiency Engineering团队在8月20日搬入了学清嘉创大厦,为庆祝团队的乔迁之喜,字节君决定邀请整个EE团队,举办一个大型团建游戏-字节跳动大闯关。可是遇到了一个问题:

EE团队共有n个人,大家都比较害羞,不善于与陌生人交流。这n个人每个人都向字节君提供了自己认识的人的名字,不包括自己。如果A的名单里有B,或B的名单里有A,则代表A与B相互认识。同时,如果A认识B,B认识C,则代表A与C也会很快的认识,毕竟通过B的介绍,两个人就可以很快相互认识的了。

为了大闯关游戏可以更好地团队写作、气氛更活跃,并使得团队中的人可以尽快的相互了解、认识和交流,字节君决定根据这个名单将团队分为m组,每组人数可以不同,但组内的任何一个人都与组内的其他所有人直接或间接的认识和交流。如何确定一个方案,使得团队可以分为m组,并且这个m尽可能地小呢?

输入描述

第一行一个整数n,表示有n个人,从1开始编号

接下来n行,分别表示第i个人认识的人的编号k(1<=k<=n),每个人的名单以0代表结束

输出描述

一个整数m,代表可以闻的最小的组的个数

示例

输入

10

0

5 3 0

8 4 0

9 0

9 0

3 0

0

7 9 0

0

9 7 0

输出

2

说明

1号同学孤独的自己一个组,他谁也不认识,也没有人认识他。其他所有人均刻意直接或间接的认识,分在同一组。

解法

思路

采用并查集。
对于图G(V,E),每个人为一个节点v,如果x号同学和y号同学认识或者间接认识,则(x,y)为图中的一条边。
set数组用来表示这个图,最后计算图set中有多少连通分支。
set[x]=x时,代表x为该连通分支的根节点,有多少根节点就有多少连通分支。

JavaScript实现

let n = parseInt(readline());
let arr = new Array();
let set = new Array();
for(let i = 0; i < n; i++){
    let line = readline().split(" ");
    arr[i] = new Array();
    set[i] = i;
    for(let j = 0; j < line.length; j++){
        arr[i][j] = parseInt(line[j]) - 1;
    }
}
for(let i = 0; i < n; i++){
    for(let j = 0; j < arr[i].length-1; j++){
        union(set, i, arr[j]);
    }
}
let count = 0;
for(let i = 0; i < n; i++){
    if(set[i] == i){
        count++;
    }
}
print(count);

//返回x的根节点
function root(set, x){
    if(set[x] != x){
        root(set[x]);
    }else{
        return x;
    }
}
//合并x和y所在的两个连通分支
function union(set, x, y){
    let xroot = root(set, x);
    let yroot = root(set, y);
    if(xroot != yroot){
        set[y] = xroot;
        set[yroot] = xroot;
    }
}

afra
225 声望23 粉丝

你的努力程度之低 远还没有到比拼天赋的需要。