标签:重点 col line code return tput res 不同 sam
有三类动物A、B、C,三类动物的食物链构成环形,A吃B,B吃C,C吃A。给定N个动物,编号1到N(1<=B<=50000),每个动物都是A、B、C的一种。按顺序给出如下的两种信息共K(0<=K<=100000)条。
然而这些信息可能出错,出错的原因有如下三种:
统计出假的信息的数量。
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
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\)个元素建立并查集。对于每一条信息,进行如下操作:
以上合并操作使得,每一个组内的所有元素代表的情况都同时发生,例如若i-A和j-B在同一组内,则说明i属于A,j属于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版)
标签:重点 col line code return tput res 不同 sam
原文地址:https://www.cnblogs.com/wtyuan/p/12179284.html