1-21并查集
一.基础知识(不多赘述)
1.查询;
2.合并;
(并查集擅长动态维护许多具有传递性的关系)
二.两大并查集:1.扩展域并查集。2:边带权并查集
1.POJ-1182(食物链)
本题选用扩展域的方法;
把每个动物拆成三个节点,同类域self,捕食域eat,天敌域enemy;
若“x与y是同类”,说明”x的同类“与“y的同类”一样,同理x,y的捕食域与天敌域也是一样的,所以只要合并x,y的self,eat,enemy
同理若“x吃y”就合并x_eat与y_self,x_self与y_enemy,x_enemy与y_eat
在处理每句话之前,都要检查这句话的真假;
有两种情况与“x与y是同类”矛盾;
a.x_eat与y_self在同一集合,说明x吃y
b.x_self与y_eat在同一集合,说明y吃x
有两种情况与“x吃y”矛盾
a.x_self与y_self在同一集合,说明x与y是同类
b.x_self与y_eat在同一集合,说明y吃x
附代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
using namespace std;
const int MAXN = 5e4 + 10;
int f[MAXN * 3], side[MAXN * 3];//三种集合,空间翻三倍
int ans = 0;
int find_set(int x) {
if (x != f[x])f[x] = find_set(f[x]);
return f[x];
}
void union_set(int x, int y) {
x = find_set(x);
y = find_set(y);
if (x != y) {
f[x] = f[y];
}
}
int main() {
int n, k;
scanf("%d%d", &n, &k);
for (int i = 1; i <= n * 3; i++) {
f[i] = i;
}
for (int i = 1; i <= k; i++) {
int d, x, y;
scanf("%d%d%d", &d, &x, &y);
if (x > n || y > n) {
ans++;
continue;
}
int x_self = x, x_eat = x + n, x_enemy = x + 2 * n;
int y_self = y, y_eat = y + n, y_enemy = y + 2 * n;
if (d == 1) {
if (find_set(x_eat) == find_set(y_self)) {
ans++;
}
else if (find_set(x_self) == find_set(y_eat)) {
ans++;
}
else {
union_set(x_self, y_self);
union_set(x_eat, y_eat);
union_set(x_enemy, y_enemy);
}
}
else {
if (find_set(x_self) == find_set(y_self)) {
ans++;
}
else if (find_set(x_self) == find_set(y_eat)) {
ans++;
}
else {
union_set(x_eat, y_self);
union_set(x_self, y_enemy);
union_set(x_enemy, y_eat);
}
}
}
printf("%d", ans);
return 0;
}
这里提一句,虽然天敌域的作用在判断真假时没有明显的表示数出来。但是在建立不同的集合时,天敌域的作用就和桥梁一样,使得eat与self得以存在同一个集合里。
2.hdu-3038
本题使用边带权并查集;
思想方法借鉴深入理解带权并查集
附AC代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
using namespace std;
const int maxn = 200000 + 10;
int f[maxn];
int sum[maxn];
int find_set(int x);
int main() {
int n, m;
while (~scanf("%d%d", &n, &m)) {
for (int i = 0; i <= n; i++) {
f[i] = i;
sum[i] = 0;
}
int x, y;
int s;
int ans = 0;
while (m--) {
scanf("%d%d%d", &x, &y, &s);
x--;
int fx = find_set(x);
int fy = find_set(y);
if (fx != fy) {
f[fy] = fx;
sum[fy] = sum[x] - sum[y] + s;
}
else if (sum[y] - sum[x] != s) {
ans++;
}
}
printf("%d\n", ans);
}
return 0;
}
int find_set(int x) {
if (x == f[x]) return x;
else {
int root = find_set(f[x]);
sum[x] += sum[f[x]];
return f[x] = root;
}
}