zoukankan      html  css  js  c++  java
  • 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)
  • 相关阅读:
    iOS9下App Store新应用提审攻略
    信息安全系统设计基础第十一周学习总结
    Linux第十一次学习笔记
    信息安全系统设计基础第十周学习总结
    Linux第八次学习笔记
    Linux第七次实验笔记
    家庭作业3.67
    Linux第六次学习笔记
    Linux第五次学习笔记
    Linux第四次学习笔记
  • 原文地址:https://www.cnblogs.com/wtyuan/p/12179284.html
Copyright © 2011-2022 走看看