2021-01-21

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;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值