并查集拓展域 POJ 1182 食物链

题目链接

题目是中文,不再写题目描述。

这个题主要说一个思路

自上而下进行操作的时候,如果遇到操作1,表示将后续的两个生物列为同一类,矛盾的条件是后面的两个生物 a ,  b 存在捕食关系,也就是说如果 a 捕食 b 或者 b 捕食 a 则是假的。

操作2而言,表示后续两个生物 a 和 b 中, a 捕食 b ,矛盾条件是 a 和 b 已知是同类生物或者已知 b 捕食 a ,以为题目说明了只有三类生物A,B,C且捕食关系成环形,因此可以列出捕食域,同类域,天敌域

操作1判断条件:

  1.  a 捕食 b ,即 a 的捕食域和 b 的同类域在同一集合
  2.  b 捕食 a ,即 b 的捕食域和 a 的同类域在同一集合

操作2判断条件:

  1. a 和 b 是同类,即 a 的同类域和 b 的同类域在同一集合
  2. b 捕食 a,即 b 的捕食域和 a 的同类域在同一集合或者 a 的天敌域和 b 的同类域在同一集合 

合并的时候,对于操作1,将 a 和 b 的同类域、捕食域、天敌域对应的合并即可,对于操作2,需要将 a 的捕食域和 b 的同类域合并,将 a 的同类域和 b 的天敌域合并,此外,根据题目描述的环形捕食关系可以看出还需要将 a 的天敌域和 b 的捕食域合并

对每一个区域的定义,是将 root 数组开大,对于题目输入的 n ,a 的同类域是 root[ n ],捕食域是 root[a + n],天敌域是root[a + n + n],这样可以错开,一开始我写的时候写出了 a,a + a,a + a + a,这样的话如果 n = 10,a = 1的时候天敌域和捕食域与其他的生物的同类域等会重复,用 n 可以跳过他们的对应区域,不会重复

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 10;
int n, k;
int root[maxn * 5];
int find(int a){
	return root[a] == a ? a : root[a] = find(root[a]);
}
void mer(int a, int b){
	int fa = find(a);
	int fb = find(b);
	if(fa != fb)root[fa] = fb;
}
int main()
{
	scanf("%d %d", &n, &k);
	for (int i = 0; i < maxn * 5; i++)root[i] = i;
	int res = 0;
	while (k--){
		int a, b, c;
		scanf("%d %d %d", &a, &b, &c);
		if(b > n || c > n)++res;
		else if(a == 1){
			if(find(b) == find(c + n) || find(b + n) == find(c))++res;
			else{
				mer(b, c);
				mer(b + n, c + n);
				mer(b + n + n, c + n + n);
			}
		}
		else if(a == 2){
			if(b == c || find(b) == find(c) || find(c + n) == find(b)){
				++res;
			}
			else{
				mer(b + n, c);
				mer(b, c + n + n);
				mer(b + n + n, c + n);
			}
		}
	}
	printf("%d\n", res);
	return 0;
}

另:该题还有另外的解法,不是用拓展域,而是用边带权的并查集,日后补上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值