持续创作,加速成长!这是我参与「掘金日新计划 · 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建边
第二步,将 1和7、3和5建边
第三步,将2和8、4和6建边
此时,没有任何冲突的边,所以,按照并查集的图,我们可以将其分为1、4和2、3两组
数组画的图可能不太清楚,给他整理为图,看一下
那么,什么时候无解呢?
假设第三步是2、3有冲突,而根据第二步的图可以得到,2和3是在一个集合中的,靠5练习起来了的 则不可能成立,即答案为假
来看一下复杂点的例子 有下列例子
[[1,2],[2,3],[3,4],[4,5],[1,5]]
在操作第五步之前时,我们的图长这样
然后,第五步会导致什么,就不用我多说了吧!
前面的说明了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
}
}