可能的二分法

85 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第19天,点击查看活动详情

描述

给定一组 n 人(编号为 1, 2, ..., n), 我们想把每个人分进任意大小的两组。每个人都可能不喜欢其他人,那么他们不应该属于同一组。 给定整数 n 和数组 dislikes ,其中 dislikes[i] = [ai, bi] ,表示不允许将编号为 ai 和  bi的人归入同一组。 当可以用这种方法将所有人分进两组时,返回 true;否则返回 false

  • 1 <= n <= 2000
  • 0 <= dislikes.length <= 10^4
  • dislikes[i].length == 2
  • 1 <= dislikes[i][j] <= n
  • ai < bi
  • dislikes 中每一组都 不同

思路

这道题,可以使用广度优先搜索或者深度优先搜索来做

但是,它还可以使用种类并查集来做这一件事情

下面先介绍一下种类并查集:

种类并查集是一种带拓展域的并查集数组

利用的思想就是,敌人的敌人是朋友,负负得正的玩法

若是a和b是敌人,则把 a 和 b+n 建边,把b 和 a+n建边

为什么要这样玩呢?

我们假设 1-n表示的是他们本身,n+1 - 2*n 表示的是他们的反面

那么,若是a和b是敌人,则就可以把a和b+n连起来,因为b的反面的同类刚好就是a

连好之后,前面的关系都一一确定了,若是到了某一对关系,明明他们是敌对是,可是他们却在一个集合中时

发生矛盾则证明逻辑不能够传递

下面举个例子来感受一下:

[[1,2],[1,3],[2,4]]

1, 2, 3, 4 | 5, 6, 7, 8

第一步,将 1和6、2和5建边 image.png 第二步,将 1和7、3和5建边

image.png 第三步,将2和8、4和6建边

image.png 此时,没有任何冲突的边,所以,按照并查集的图,我们可以将其分为1、4和2、3两组

数组画的图可能不太清楚,给他整理为图,看一下

image.png

那么,什么时候无解呢?

假设第三步是2、3有冲突,而根据第二步的图可以得到,2和3是在一个集合中的,靠5练习起来了的 则不可能成立,即答案为假

来看一下复杂点的例子 有下列例子

 [[1,2],[2,3],[3,4],[4,5],[1,5]]

在操作第五步之前时,我们的图长这样

image.png

然后,第五步会导致什么,就不用我多说了吧!

前面的说明了1和5是同类,结果它说不是同类,那么一定不成立

代码

impl Solution {
    pub fn possible_bipartition(n: i32, dislikes: Vec<Vec<i32>>) -> bool {
        let mut f = vec![0; (2*n+1) as usize];
        for i in 0..=(2*n) {
            f[i as usize] = i
        }
        fn dfs(f: &mut Vec<i32>, x: usize) -> i32 {
            if f[x] as usize != x {
                f[x] = dfs(f, f[x] as usize);
            }
            f[x]
        }
        for v in &dislikes {
            let (a, b) = (dfs(&mut f, v[0] as usize), dfs(&mut f, v[1] as usize));
            if a == b {
                return false;
            }
            let (x, y) = (
                dfs(&mut f, (v[0] + n) as usize),
                dfs(&mut f, (v[1] + n) as usize),
            );
            f[x as usize] = b;
            f[y as usize] = a;
        }
        true
    }
}