POJ1182 食物链 解题报告

来源:互联网 发布:传智播客java全套视频 编辑:程序博客网 时间:2024/05/16 11:28
【问题描述】  
  
  动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。


  现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。有人用两种说法对这N个动物所构成的食物链关系进行描述: 


  第一种说法是“1 X Y”,表示X和Y是同类。
  第二种说法是“2 X Y”,表示X吃Y。




  此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。 


  1)当前的话与前面的某些真的话冲突,就是假话;
  2)当前的话中X或Y比N大,就是假话;
  3)当前的话表示X吃X,就是假话。


  你的任务是根据给定的N和K句话,输出假话的总数。 
 
    
 【输入格式】  
  
  第一行是两个整数N和K,以一个空格分隔。
  以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
  若D=1,则表示X和Y是同类。
  若D=2,则表示X吃Y。


 
    
 【输出格式】  
   
  只有一个整数,表示假话的数目。
 
    
 【输入样例】   
   
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5


 
    
 【输出样例】  
   
3


 
    
 【数据范围】  
   
1<=N<=50,000
0<=K<=100,000


 
    
 【来源】  
  
对输入的7句话的分析如下:
100 7 
1 101 1   假话
2 1 2   真话
2 2 3   真话
2 3 3   假话
1 1 3   假话
2 3 1   真话
1 5 5   真话


 解题思路:根据题意,可以运用并查集的思想来解决此题,对于每次输入的话,先判断输入的X与Y的关系是否已知,如果未知,则将X与Y存入并查集中,并注明X与Y的关系;如果已知,则判断已知关系与所输入的关系是否相同(注意,如果X>N或Y>N,则输入的话是假话)。本题的重点及难点在于,已知X的关系和Y的关系,如何将他们的关系合并,要具体情况具体分析,下面就本题出现的合并关系的情况一一进行分析。先设pa[i]==j(i的父亲是j),re[i]==0表示i与j是同类,re[i]==1表示i被j吃,re[i]==2表示i吃j。

①在寻找x所在集合的代表元(根),进行路径压缩时,将x的关系与pa[x](x的父亲)的关系进行合并。

通过上图,可以看出当x被pa[x]吃,而pa[x]吃root(根)时,x与root同类。同理,多列举几种可能,可以得出此时的关系合并的通式为re[x]=(re[x]+re[pa[x]])%3。


②在通过输入数据确定了x与y的关系后,将x的根与y的根(路径压缩后,即为他们的父亲)的关系进行合并。


通过上图,可以看出若输入的话为x吃y(y被x吃),而假设y与py是同类,x吃px,则px与py是同类。同理,多列举几种可能,设合并时py的父亲改为px,可以得出通式为re[py]=(re[x]-re[y]+d+3)%3(这里的d为根据输入得出的y对于x的关系)。


③在输入每句话,判断X与Y的关系是否已知,如果X与Y的关系未知(X与Y的根不同),则返回3,如果已知关系,则返回已知的Y与X的关系。(px、py分别为x和y的根,路径压缩后即为他们的父亲)(注,这里返回的是Y对于X的关系)


通过上图,可以看出若y被py吃,x吃px,y吃x。同理,多列举几种可能,可以得出通式返回的值为(re[y]-re[x]+3)%3。


#include<cstdio>#include<cstdlib>#include<iostream>#include<algorithm>#include<cstring>#include<queue>#include<vector>#include<cmath>using namespace std;const int maxn=50005;int N,K,d,x,y,ans=0;int pa[maxn],re[maxn];  //设pa[i]==j(i的父亲是j),re[i]==0表示i与j是同类,re[i]==1表示i被j吃,re[i]==2表示i吃jvoid initial()  //并查集初始化{for(int i=1;i<=N;i++)pa[i]=i,re[i]=0;}int find(int x){if(pa[x]==x)  return x;int root=find(pa[x]);re[x]=(re[x]+re[pa[x]])%3;  //重点pa[x]=root;return root;}int judge(int x,int y)  //0同类  1被吃  2吃  3不知道{int px=find(x);int py=find(y);if(px!=py)  return 3;  return (re[y]-re[x]+3)%3;  //重点,难点,返回的是y对于x的关系}void bing(int x,int y,int d)  //这里的d为y对于x的关系{int px=find(x);int py=find(y);pa[py]=px;re[py]=(re[x]-re[y]+d+3)%3;  //重点,难点}int main(){//freopen("48.in","r",stdin);//freopen("48.out","w",stdout);scanf("%d%d",&N,&K);initial();for(int i=1;i<=K;i++){scanf("%d%d%d",&d,&x,&y);int t=judge(x,y);if(x>N || y>N)  {ans++;continue;}if(t==3)  //关系未知{bing(x,y,d-1);continue;}if(t!=d-1)  ans++;  //因为返回的是y对于x的关系,这里才可以用t!=d-1来判断}printf("%d\n",ans);return 0;}


1 0
原创粉丝点击