五月集训(第26天)—并查集
并查集
1. 990. 等式方程的可满足性
思路:
如果 a==b
则将a,b放入同一个集合(union)。最后判断,如果出现a!=b
,而a,b在同一个集合里的情况,矛盾,返回false;否则,返回true。
class Solution {
#define maxn 30
int F[maxn];
public:
// 初始化并查集
void Build(int n) {
for (int i = 0; i < n - 1; ++i) F[i] = i; // 初始化
}
// 查找父节点 (路劲压缩)
int find_f(int x) {
if (F[x] != x) return F[x] = find_f(F[x]);
return x;
}
// 集合合并
void union_set(int a, int b) {
int fa = find_f(F[a]);
int fb = find_f(F[b]);
if (fa == fb) return ;
F[fa] = fb;
}
bool equationsPossible(vector<string>& equations) {
int equations_size = equations.size();
int i;
Build(26); /* 建立并查集 */
for (i = 0; i < equations_size; ++i) {
if (equations[i][1] == '=') union_set(equations[i][0] - 'a', equations[i][3] - 'a');
}
for (i = 0; i < equations_size; ++i) {
if (equations[i][1] == '!') if (find_f(equations[i][0] - 'a') == find_f(equations[i][3] - 'a')) return false;
}
return true;
}
};
2. 1319. 连通网络的操作次数
思路:
如果connections.size() - 1 > 集合个数
,随便拆一条边连接到集合与集合直接即可
特判n - 1 > connections.size()
,边的个数少于点的个数 - 1,则一定不能实现完全连通。
class Solution {
#define maxn 100010
int F[maxn];
bool F_flag[maxn];
public:
void Build(int n) {
for (int i = 0; i < n; ++i) F[i] = i;
memset(F_flag, 0, sizeof(F_flag));
}
int find_f(int x) {
if (F[x] == x) return x;
return F[x] = find_f(F[x]);
}
void union_set(int a, int b) {
int fa = find_f(a);
int fb = find_f(b);
if (fa != fb) F[fa] = fb;
return ;
}
int makeConnected(int n, vector<vector<int>>& connections) {
int connections_size = connections.size();
int set_cnt = 0;
Build(n);
// 特判:边不够一定不能连接所有顶点
if (n - 1 > connections_size) return -1;
// 创建集合关系
for (int i = 0; i < connections_size; ++i) {
union_set(connections[i][0], connections[i][1]);
}
// 统计集合个数
for (int i = 0; i < n; ++i) {
int f_i = find_f(i);
if (!F_flag[f_i]) {
set_cnt++;
F_flag[f_i] = true;
}
}
return set_cnt - 1;
}
};
3. 886. 可能的二分法
思路:
首先考虑将一个人a放入集合A,把他讨厌的人b放进集合B,后面再出现一个人c,将他放入集合A,将他讨厌的人d放入集合B,但是如果a讨厌c,那么不仅要把c从集合A放入B,还要把c讨厌的d放入集合A,当c有很多讨厌的人,就要逐个找出来放入A中,很麻烦。
于是考虑对每个人创造一个对应的讨厌的人作为桥梁,将a与b讨厌的人b+n放入一个集合,将b与a讨厌的人a+n放入一个集合,如果再出现c,解可以利用a+n将c与b放入同一个集合,这样通过X+n这个媒介就可以直接实现将一个人与其讨厌的人交换集合的操作,很方便。
class Solution {
#define N 2010
int F[N << 1]; // 开两倍空间,0~N存每个人,N+1~2*N存每个人讨厌的人
public:
void Build(int n) {
int m = 2 * n;
memset(F, 0, sizeof(F));
for (int i = 0; i < m; ++i) F[i] = i;
}
void union_set(int a, int b) {
int fa = find_f(a);
int fb = find_f(b);
if (fa != fb) F[fa] = fb;
}
int find_f(int x) {
if (x == F[x]) return x;
return F[x] = find_f(F[x]);
}
bool possibleBipartition(int n, vector<vector<int>>& dislikes) {
int dislikes_size = dislikes.size();
Build(n);
for (int i = 0; i < dislikes_size; ++i) {
int a = dislikes[i][0], b = dislikes[i][1];
union_set(a, b + n);
union_set(b, a + n);
if (find_f(a) == find_f(b)) return false;
}
return true;
}
};
东方欲晓,莫道君行早。