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 imes 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
", cnt);
	system("pause");
	return 0;
}
## 参考:
[1] [《挑战程序设计竞赛》(第2版)](http://product.dangdang.com/23272528.html)
原文地址:https://www.cnblogs.com/wtyuan/p/12179284.html