poj1182 食物链 并查集应用

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

//用3*N的空间建立并查集,0~N-1,N~2N-1,2N~3N-1三个区间分别表示ABC三个种类
//同类:若xy同类,合并xy说明xy属于A类,当然也可能属于B,C类,此时合并x+N,y+N和x+2N,y+2N表示他们分别属于B和C类
//捕食:若x吃y, 合并x和y+N,表示A中x吃B中y,当然也可能Bx吃Cy,Cx吃Ay,此时用合并x+N,y+2N和y+2N来表示(不会再出现Bx吃Ay等,因为已经有Ax吃By,两种情况只能发生一种)
//并查集中合并在一组的所有元素代表情况同时发生或不发生 比如:A中i吃B中j,就表示如果i属于A,j一定属于B 或 如果j属于B,i一定属于A
//合并前先进行判断如果出现矛盾,就表示此语句是错误信息 
//判断:1数据范围 2合并同类关系时,判断是否有捕食关系 3合并捕食关系,判断是否有同类关系和反捕食关系 
#include <cstdio>
#include <cstring>
const int MAX_N = 50010;
const int MAX_K = 100010;
int N, K;
int T[MAX_K], X[MAX_K], Y[MAX_K];	//输入(T是信息的类型)
int par[MAX_N * 3];
int rank[MAX_N * 3];
void init(int n){	//并查集初始化n个元素
	for(int i = 0;i < n;i ++){
		par[i] = i;
		rank[i] = 0;
	}
}
int find(int x){	//并查集查找 
	return par[x] == x ? x : par[x] = find(par[x]);
}
void unite(int x, int y){	//合并 
	x = find(x);
	y = find(y);
	if(x == y)	return ;
	if(rank[x] < rank[y]){
		par[x] = y;
	}else{
		par[y] = x;
		if(rank[x] == rank[y])	rank[x] ++;
	}
}
bool same(int x, int y){	//根是否相同(同类或者捕食关系) 
	return find(x) == find(y);
}
void solve(){
	init(N * 3);//初始化并查集,元素x,x+N,x+2*N分别代表x-A,x-B,x-C (x-A代表x属于种类A) 
	int ans = 0;
	for(int i = 0;i < K;i ++){
		int t = T[i];
		int x = X[i] - 1, y = Y[i] - 1;//把输入变成0,...,N-1的范围
		if(x < 0 || N <= x || y < 0 || N <= y){//不正确的编号
			ans ++;
			continue ;
		}
		if(t == 1){	//"x和y属于同一类"的信息 
			if(same(x, y + N) || same(y, x + N)){//Ax吃By或者Ay吃Bx
				ans ++;
			}else{
				unite(x, y);	//x与y同属A类,下同 
				unite(x + N, y + N);	//x与y同属B类,下同 
				unite(x + 2 * N, y + 2 * N);	//x与y同属C类,下同 
			}
		}else{	//"x吃y"的信息
			if(same(x, y) || same(y, x + N)){	//Ax与Ay同类,或者Ay吃Bx
				ans ++;
			}else{
				unite(x, y + N);		//Ax吃By 
				unite(x + N, y + 2 * N);//Bx吃Cy 
				unite(x + 2 * N, y);	//Cx吃Ay
			}
		}
	}
	printf("%d\n", ans );
}
int main(){
	scanf("%d%d", &N, &K);
	for(int i = 0;i < K;i ++)
		scanf("%d%d%d", &T[i], &X[i], &Y[i]);
	solve();
	return 0;
} 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值