POJ NO.1182 食物链(并查集,带权并查集最优解,没有之一)

本文介绍了一个有趣的问题,即如何通过一系列描述来确定一组动物间的食物链关系,并判断这些描述的真实性。通过对不同种类动物间的捕食关系建模,利用并查集算法解决冲突检测问题。

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

问题描述:

动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。 
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。 
有人用两种说法对这N个动物所构成的食物链关系进行描述: 
第一种说法是"1 X Y",表示X和Y是同类。 
第二种说法是"2 X Y",表示X吃Y。 
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。 
1) 当前的话与前面的某些真的话冲突,就是假话; 
2) 当前的话中X或Y比N大,就是假话; 
3) 当前的话表示X吃X,就是假话。
 
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。 

题目链接:POJ NO.1182

思路:

我们用x\y, x\y + N, x\y + 2 * N 分别表示A、B、C三类。

这样只需判断x...,y...是否在一个组里即可根据类型信息判断是否是实话。

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<stack>
#include<cstring>
#include<string>
#include<vector>
#include<set>
using namespace std;

#define X first
#define Y second
#define PI 3.1415926
const int MAX_N = 5e4 + 100;
const int MAX_K = 1e5 + 100;
const int INF = 0x3f3f3f3f;

int N, K;
int par[3*MAX_N];
int T[MAX_K], X[MAX_K], Y[MAX_K];

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

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

void unite(int x, int y){
    x = find(x);
    y = find(y);
    if(x == y){
        return ;
    }else
        par[x] = y;
}

bool same(int x, int y){
    return find(x) == find(y);
}


int main(){
    scanf("%d%d", &N, &K);
    init(3 * N);
    for(int i = 0; i < K; i++){
        scanf("%d%d%d", &T[i], &X[i], &Y[i]);
    }
    int ans = 0;
    for(int i = 0; i < K; i++){
        int t = T[i];
        int x = X[i] - 1, y = Y[i] - 1;
        if(x < 0 || N <= x || y < 0 || N <= y){
            ans++;
            continue;
        }
        if(t == 1){
           /*
            same(x, y + N)已经囊括了
            unite(x, y + N);
            unite(x + N, y + 2 * N);
            unite(x + 2 * N, y);
            这三种,他们是分不开的。
            下面的same(x, y),也是同样的道理。

            same(x, y + 2 * N)(A,C)则表示相隔两类。
            这样四种情况“压缩成了两种”
            
            下面是同样的道理
                                   */
            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{
            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、付费专栏及课程。

余额充值