算法题(189):食物链
审题:
本题需要我们通过维护食物链关系并判断给出的指令的真伪,将所有假指令的数目统计出来并输出思路:
方法一:扩展域并查集本题总共有三种动物,分别为A,B,C。三者构成环形捕食关系,即A->B,B->C,C->A
给出的指令有两种:其一是x与y是同类,其二是x捕食y
而指令满足以下条件任意一条就是假指令
1.与前面真指令冲突(可能出现在两种指令中)
2.x或y比n大(可能出现在两种指令中)
3.同类相食(只可能出现在捕食指令中)
故冲突情况(假指令):
对两种指令:出现x或y比n大
若为同类指令:出现X->Y/Y->X
若为捕食指令:出现Y->X/X==Y(同类关系)
非冲突情况维护方法(真指令):
1.同类指令(x,y为同类):此时x和y的同类域,捕食域和天敌域都要合并
也就是x,y集合合并,x+n,y+n集合合并,x+2n,y+2n集合合并
2.捕食指令(x捕食y):此时我们需要画图分析各域分布情况再决定如何合并集合
先分析x的域分布:
x在A处,y在B处,那么由于x捕食y,所以x+n在B处,x+2n在C处
y的域分布:
y在B处,由于y被x捕食,所以y+2n在A处,y+n在C处
总思路:
将区域分为同类域(x),捕食域(x+n),天敌域(x+2n)。依次遍历每一条指令,判断指令真伪,若为真则维护食物链关系,若为假则让ret++,表示假指令又多了一条。最后输出ret即可
解题:
#include<iostream> using namespace std; const int N = 5e4 + 10; int fa[3 * N]; int n, k,ret; int find(int x) {return fa[x] == x ? x : fa[x] = find(fa[x]); } void un(int x, int y) {fa[find(x)] = find(y); } bool judge(int x, int y) {return find(x) == find(y); } int main() {cin >> n >> k;//扩展域并查集初始化for (int i = 1; i <= 3 * n; i++){fa[i] = i;}for (int i = 1; i <= k; i++){int a, b, c;cin >> a >> b >> c;if (b > n || c > n){ret++;continue;}if (a == 1)//同类关系{//出现b->c 或 c->b是假话if (judge(b+n, c) || judge(c+n,b)){ret++;}else{un(b, c);un(b + n, c + n);un(b + 2 * n, c + 2 * n);}}else//b捕食c的关系{//出现b和c是同类 或 c->bif (judge(b, c + n) || judge(b,c)){ret++;}else{un(b, c + 2 * n);un(b + n, c);un(c + n, b + 2 * n);}}}cout << ret << endl;return 0; }
P2024 [NOI2001] 食物链 - 洛谷