POJ1182 食物链 并查集

POJ1182

题目

  有三类动物A、B、C,三类动物的食物链构成环形,A吃B,B吃C,C吃A。给定N个动物,编号1到N(1<=B<=50000),每个动物都是A、B、C的一种。按顺序给出如下的两种信息共K(0<=K<=100000)条。

  1. x和y属于同一类
  2. x吃y

  然而这些信息可能出错,出错的原因有如下三种:

  1. 当前的信息与前面的某些真的信息冲突,就是假的信息
  2. 当前的信息中,x与y比N大
  3. 当前的话表示x吃x

  统计出假的信息的数量。

Sample Input

100 7
1 101 1 
2 1 2
2 2 3 
2 3 3 
1 1 3 
2 3 1 
1 5 5

Sample Output

3

算法思路

  第2、3类错误原因很容易处理,重点是如何处理第1类错误原因,当处理当前信息时,我们需要知道当前信息是否与之前的真的信息有冲突。假如当前信息表明,x与y是同类,则我们需要判断之前的真的信息中,是否有信息表明x与y不同类(即x吃y,或y吃x),假如当前信息表明x吃y,我们需要判断之前的真的信息中,是否有信息表明x与y同类或y吃x。那么如何实现上述操作呢?每来一个信息,我们只知道x与y的关系,而不知道x、y分别属于哪一个类,而每一种关系都可以对应三种情况。若x与y同类,则x和y为A,为B,为C都可以。若x吃y,则x为A,y为B,x为B,y为C,x为C,y为A这三种情况都可以。那如何处理呢?答案是三种情况均考虑到。

  参考《挑战程序设计竞赛》(第2版)中的思路,采用特殊的并查集来解决这个问题对于每只动物i创建三个元素i-A,i-B,i-C(分别表示i属于A、B、C),并用这\(3\times N\)个元素建立并查集。对于每一条信息,进行如下操作:

  1. x和y属于同一类,则合并x-A和y-A,x-B和y-B、x-C和y-C,共进行三次合并,将上文中的三种情况都考虑到了。
  2. x吃y,则合并x-A和y-B,x-B和y-C、x-C和y-A,同样把上文中的三种情况都考虑到了。

  以上合并操作使得,每一个组内的所有元素代表的情况都同时发生,例如若i-A和j-B在同一组内,则说明i属于A,j属于B是同时发生的。不过在合并之前,需要确定是否与之前的真的信息产生矛盾:

  1. x和y属于同一类,则需要判断之前是否有真的信息表明x吃y或y吃x。即x-A、y-B是否是同一组或y-A、x-B是否是同一组。
  2. x吃y,则需要判断之前是否有真的信息表明x与y同类或y吃x,即x-A、y-A是否是同一组或y-A、x-B是否是同一组。

  由于合并操作每次把三种情况都考虑到了,所以判断时只需要对一种情况判断即可。

代码

Result: 1196kB, 297ms

#include <stdio.h>
#include <iostream>

#define N 50000
int n, k;
int parent[3 * N + 10];

void MakeSet() {
    for (int i = 1; i <= 3 * N; i++)
        parent[i] = i;
}

int Find(int x) {//路径压缩
    while (x != parent[x])
        x = parent[x];
    return x;
}

void Union(int x, int y) {
    int r = Find(x), s = Find(y);
    if (r == s)
        return;
    else
        parent[r] = s;
}

int main() {
    scanf("%d %d", &n, &k);
    int d, x, y, cnt = 0;
    MakeSet();
    while (k--) {
        scanf("%d %d %d", &d, &x, &y);
        if (x > n || y > n)//第二种信息
            cnt++;
        else if (d == 2 && x == y)//第三种信息
            cnt++;
        else {//第一种信息
            if (d == 1) {//x与y同类
                if (Find(x) == Find(y + n) || Find(y) == Find(x + n))//不允许之前的真的信息表明x吃y或y吃x
                    cnt++;
                else {
                    Union(x, y);
                    Union(x + n, y + n);
                    Union(x + 2 * n, y + 2 * n);
                }
            }
            else {//d = 2,x吃y
                if (Find(x) == Find(y) || Find(y) == Find(x + n))//不允许之前的真的信息中表明x与y同类或y吃x
                    cnt++;
                else {
                    Union(x, y + n);
                    Union(x + n, y + 2 * n);
                    Union(x + 2 * n, y);
                }
            }
        }
    }
    printf("%d\n", cnt);
    system("pause");
    return 0;
}

```

参考:

[1] 《挑战程序设计竞赛》(第2版)

猜你喜欢

转载自www.cnblogs.com/wtyuan/p/12179284.html