题目
思路
这种谁和谁一组,最后又要求是否能分组的问题,一眼并查集,但本题与最基础的并查集把同一种类的集合起来不同,他是把两种不同种类(互斥)的集合起来,其中的思想大概就是敌人的敌人就是朋友,这种并查集又称种类并查集。
并查集最基础的两个组件一个是find,一个是union。前者是找到当前元素所在集合的代表元素,给个比喻就是找到这个组员的组长,后者是合并,将两个组合并,实现方法就是将一个组的组长的组长变成另一个组的组长(原本每个组的组长都是他自己)。其中f[a]中储存的就是a的组长,在最开始每个成员的组长都是他自己。
int find(int a) {
if(f[a] == a) return a;
return f[a] = find(f[a]);
}
void uni(int a, int b) {
f[find(a)] = find(f[b]);
}
基础的并查集就是每次读入两个人,将两个人合并。最后要查找某两个人是否有关系(find[a] = find[b]),然后给出答案。
该题中我们每次读入两个人后,就不能直接将两个人合并,而是分别将本人a与敌人b,本人b与敌人a合并,如果发现本人a与本人b有相同的敌人,那么就不可能将人分成两组,返回false。实现这样的功能我们就需要两倍n大的f数组。
代码如下
class Solution {
public:
int f[4002];
bool possibleBipartition(int n, vector<vector<int>>& dislikes) {
for(int i = 0; i < n*2; i ++) {
f[i] = i;
}
for(int i = 0; i < dislikes.size(); i ++) {
//是否有共同的敌人
if(find(dislikes[i][0]) == find(dislikes[i][1])) {
return false;
}
//将本人与敌人结合
uni(dislikes[i][0], dislikes[i][1] + n);
uni(dislikes[i][0] + n, dislikes[i][1]);
}
return true;
}
int find(int a) {
if(f[a] == a) return a;
return f[a] = find(f[a]);
}
void uni(int a, int b) {
f[find(a)] = find(f[b]);
}
};
结果
总结
这是一道很模板的种类并查集,可以算是让我复习了相关的概念和实现方法。当然这题也可以使用搜索的方法来解决,思路会相对简单,但结果效率上应该就没并查集这么优秀了。