POJ 1182 食物链 (并查集)

本文介绍了一种使用并查集数据结构解决动物种类归属与捕食关系问题的方法。通过巧妙地组织并查集中的元素,可以有效地处理大规模数据集上的关系维护与矛盾检测。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:http://poj.org/problem?id=1182


在挑战程序设计竞赛上看到的,方法很巧妙。

由于N和K很大,所以必须高效地维护动物之间的关系,并快速判断是否产生了矛盾。并查集是维护“属于同一组” 的数据结构,但是在本题中,并不只有属于同一类的信息,还有捕食关系的存在。因此需要开动脑筋维护这些关系。

对于每只动物i创建3个元素i-A, i-B, i-C, 并用这3*N个元素建立并查集。这个并查集维护如下信息:

① i-x 表示“i属于种类x”。

②并查集里的每一个组表示组内所有元素代表的情况都同时发生或不发生。

例如,如果i-A和j-B在同一个组里,就表示如果i属于种类A那么j一定属于种类B,如果j属于种类B那么i一定属于种类A。因此,对于每一条信息,只需要按照下面进行操作就可以了。

1)第一种,x和y属于同一种类———合并x-A和y-A、x-B和y-B、x-C和y-C。

2)第二种,x吃y—————————合并x-A和y-B、x-B和y-C、x-C和y-A。

不过在合并之前需要先判断合并是否会产生矛盾。例如在第一种信息的情况下,需要检查比如x-A和y-B或者y-C是否在同一组等信息。


#include <iostream> 
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX_N = 3 * 50100;
const int MAX_K = 100100;
int f[MAX_N];
int h[MAX_N]; //记录树的高度避免退化
int T[MAX_K], X[MAX_K], Y[MAX_K];

void init(int n)  {
	int i;
	for(i = 0; i < n; i++) {
		f[i] = i;
		h[i] = 0;
	}
}

int find(int x) {
	if(f[x] == x) return x;
	else return f[x] = find(f[x]);
}

void unite(int x, int y) {
	x = find(x);
	y = find(y);
	if(x != y) {
		if(h[x] < h[y]) f[x] = y;
		else {
			f[y] = x;
			if(h[x] == h[y]) h[x]++;  //高度变化 
		}
	}
}

bool same(int x, int y) {
	return find(x) == find(y);
}
int main() {
	int n, k;
	scanf("%d %d", &n, &k);
	int i, j;
	init(n * 3);
	int x, y, z;
	for(i = 0; i < k; i++) {
		scanf("%d %d %d", T + i, X + i, Y + i);
	}
	int ans = 0;
	for(i = 0; i < k; i++) {
		int t = T[i];
		int x = X[i] - 1, y = Y[i] - 1;
		
		if(x < 0 || x >= n || y < 0 || y >= n) {
			ans++;
			continue;
		}
		
		if(t == 1) {
			//需要判断以下6种情况,都是有捕食关系的,但是发现建立捕食关系时
			//都是3个为一组建立的,所以只需要判断2种情况就可包括6种了 
			//same(x, y + n) || same(x, y + 2 * n) ||same(x + n, y) || 
			//same(x + n, y + 2 * n) || same(x + 2 * n, y) || same(x + 2 * n, y + n)
			if(same(x, y + n) || same(x, y + 2 * n)) {
				ans++;
			}
			else {
				unite(x, y);
				unite(x + n, y + n);
				unite(x + 2 *n, y + 2 * n);
			}
		}
		else {
			//同上也可把以下6种情况简化为2种 
			//same(x, y) || same(x, y + 2 * n) || same(x + n, y + n) || 
			//same(x + 2 * n, y + 2 * n) || same(x + n, y) || same(x + 2 * n)
			if(same(x, y) || same(x, y + 2 * n)) {
				ans++;
			}
			else {
				unite(x, y + n);
				unite(x + n, y + 2 * n);
				unite(x + 2 * n, y);
			}
		}	
	}
	printf("%d\n", ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值