886. 可能的二分法

85 阅读2分钟

题目:
给定一组 n 人(编号为 1, 2, ..., n), 我们想把每个人分进任意大小的两组。每个人都可能不喜欢其他人,那么他们不应该属于同一组。

给定整数 n 和数组 dislikes ,其中 dislikes[i] = [ai, bi] ,表示不允许将编号为 ai 和  bi的人归入同一组。当可以用这种方法将所有人分进两组时,返回 true;否则返回 false

算法:
方法一: dfs 统计每个人所有不喜欢的节点,然后dfs对节点进行染色,如果出现染色冲突(已染色为1,当前dfs又染色为2),则说明存在冲突。

func possibleBipartition(n int, dislikes [][]int) bool {
    // 初始化不喜欢数组
    dislikes2 := make([][]int, n)
    for i := range dislikes {
        x, y := dislikes[i][0] - 1, dislikes[i][1] - 1
        dislikes2[x] = append(dislikes2[x], y)
        dislikes2[y] = append(dislikes2[y], x)
    }
    peopleColors := make([]int, n)
    // 将节点i 染色为color
    var dfs func (i, color int) bool 
    dfs = func (i, color int) bool {
        peopleColors[i] = color
        for _, people := range dislikes2[i] {
            if peopleColors[people] == color {
                return false
            }
            if peopleColors[people] == 0 && !dfs(people, 3 - color) {
                return false
            }
        }
        return true
    }
    for i := range dislikes2 {
        if peopleColors[i] == 0 && !dfs(i, 1) {
                return false
        }
    }
    return true
}

方法二:bfs
初始化color数组,保存每个人的颜色,0未染色,1红色,2蓝色
初始化数组g保存每个人不喜欢的所有人。
遍历g,将不喜欢的人染成对立颜色,如果发现不喜欢的人和自己是同一颜色,存在冲突

func possibleBipartition(n int, dislikes [][]int) bool {
    color := make([]int, n)
    g := make([][]int, n)
    for i := range dislikes {
        x, y := dislikes[i][0] - 1, dislikes[i][1] - 1
        g[x] = append(g[x], y)
        g[y] = append(g[y], x)
    }
    // 用栈stack代替了递归,本质上还是要递归染色
    // q出栈了意味着他不喜欢的人的链,递归完了  
    for i := range color {
        if color[i] == 0 {
            color[i] = 1
            stack := []int{i}
            for len(stack) != 0 {
                q := stack[0]
                stack = stack[1:]
                for _, j := range g[q] {
                    if color[j] == color[q] {
                        return false
                    }
                    if color[j] == 0 {
                        color[j] = 3 - color[q]
                        stack = append(stack, j)
                    }
                }
            }
            

        }
    }
    return true
}

方法三:并查集
思路:将一个人不喜欢的所有人放到同一个联通分量,如果这个人也在这个联通分量中,则说明出现冲突。

func possibleBipartition(n int, dislikes [][]int) bool {
    parent := make([]int, n)
    g := make([][]int, n)
    for i := range dislikes {
        x, y := dislikes[i][0] - 1, dislikes[i][1] - 1
        g[x] = append(g[x], y)
        g[y] = append(g[y], x)
    }
    for i := range parent{
        parent[i] = i
    }
    var find func(x int) int
    find = func(x int) int {
        if parent[x] != x {
            parent[x] = find(parent[x])
        }
        return parent[x]
    }

    for i := range g {
        for _, j := range g[i] {
            if find(i) == find(j) {
                return false
            }
            parent[find(j)] = find(g[i][0])
        }
    }
    return true
}