POJ 1182 食物链 并查集经典

本题可用并查集来解题。

假信息有如下几种情况:

(1)数据范围错误:

即:如果输入的动物编号小于0或者大于n,这显然是不符合题目条件的

(2)前后矛盾(这里认为最前面的是对的,而后面添加的与前面矛盾的信息是错误的)

即:如果x与y同类但x吃y或者y吃x,则矛盾!如果x吃y但是y又吃x,矛盾!

 

分析:

动物一共有三种,不妨设为A, B, C. 其中,A吃B, B吃C, C吃A

那么,1 x y 即:x吃y有三种可能性:x为A, y为B; x为B, y为C; x为C, y为A

同理,2 x y即:x与y是同类也有三种情况:x与y均为A; x与y均为B; x与y均为C

那么,我们就将这些情况全部枚举,并加入集合中。

这里要开一个3 * N的数组,其中[0, N - 1]记录A类,[N, 2N - 1]记录B类,[2N, 3N-1]记录C类

 

这里要注意的是,数组一定要开大!过小的话,虽然算法是对的,但是测试数据过大时会爆数组。

C++代码如下:

 

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int MAX_N = 3 * 50010;
const int MAX_K = 100010;
int par[MAX_N]; // 父亲
int Rank[MAX_N]; // 树的高度
int T[MAX_K], X[MAX_K], Y[MAX_K];
int N, K;

// 初始化n个元素
void init(int n) // 一定不要忘了初始化!!!
{
    for (int i = 0; i < n; i++)
    {
        par[i] = i; // 初始化i的父亲:一开始,每个节点都是自己的祖先
        Rank[i] = 0; // 初始化i的高度,一开始,每个节点都是自己的祖先即树根,而树根的高度为0
    }
}

// 查询并返回树的根
int find(int x)
{
    if (par[x] == x)
        return x;
    else
        return par[x] = find(par[x]); // return a = b; 的意思是,将b的值赋值给a,然后返回a的值
                                      // 这里的意思是将x的父亲的父亲赋值给x的父亲,也就是已知沿着树干向上找根节点
}

// 合并x和y所在的集合
void unite(int x, int y)
{
    x = find(x); y = find(y); // 将x和y的根分别赋值给x和y
    if (x == y) return; // 如果x于y的根相同,则不必合并

    if (Rank[x] < Rank[y]) /// 合并时如果两棵树的高度不同,则将Rank小的连到Rank到的上面
        par[x] = y;
    else
    {
        par[y] = x;
        if (Rank[x] == Rank[y]) Rank[x]++; // 如果相同,则最高的高度合并之后需要在原来的基础上+1
    }
}

bool same(int x, int y) // 判断x和y是否属于同一个集合
{
    return find(x) == find(y); // 只需判断x和y的根节点是否相同
}

void solve()
{
    init(N * 3);
    int ans = 0;
    for (int i = 0; i < K; i++)
    {
        int t = T[i];
        int x = X[i] - 1, y = Y[i] - 1; // 这样就能够出现编号为0的编号了,并且没有编号为1的编号了

        if (x < 0 || x >= N || y < 0 || y >= N) // 如果x或者y的编号越界
        {
            ans++;
            continue;
        }
        if (t == 1) // 如果1 x y即:本条给定的信息是:x与y是同类
        {
            if (same(x, y + N) || same(x, y + 2 * N)) // 如果x吃y并且y也吃x,矛盾!说明此条信息有误
                ans++;
            else // 否则,x与y为同类的动物,则合并他们
            {
                unite(x, y);
                unite(x + N, y + N);
                unite(x + 2 * N, y + 2 * N);
            }
        }
        else // 2 x y 代表x吃y
        {
            if (same(x, y) || same(x, y + 2 * N)) // 如果x与y是同类或者y吃x,又矛盾,说明此条信息有误
                ans++;
            else // 否则,x可以吃y,则将这条关系添加到集合中
            {
                unite(x, y + N);
                unite(x + N, y + 2 * N);
                unite(x + 2 * N, y);
            }
        }

    }
    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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值