食物链
时间限制:
1000 ms | 内存限制:
65535 KB
难度:
5
-
描述
-
动物王国中有三类动物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(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。
这道题可以算的上是很经典的并查集的题目了,刚开始做的时候也没思路啊,各种wa啊,后来参考了大神的思路http://blog.csdn.net/niushuai666/article/details/6981689,也看了好久,才把那个更新域搞懂。
这个题目,最重要的是处理关系域的更新,两个集合合并了之后,合并成一个集合,之间的关系就变了,就要更新他们之间的关系,其他的地方就和普通的并查集一样,这里才是并查集的本质吧。
- #include <stdio.h>
- #define MAXN 10000
- struct node
- {
- int father;
- int relation;//定义他们之间的关系
- };
- node s[MAXN];
- int find(int x)//带动态压缩的查找
- {
- int temp;
- if(x==s[x].father)
- return x;
- temp=s[x].father;
- s[x].father=find(temp);
- s[x].relation=(s[x].relation+s[temp].relation)%3;//关系域的更新,要找准他们之间的关系
- return s[x].father;
- }
- int main()
- {
- int n,k,d,x,y,i,sum=0;
- int root1,root2;
- scanf("%d%d",&n,&k);
- for(i=0;i<n;i++)//初始化
- {
- s[i].father=i;
- s[i].relation=0;
- }
- for(i=0;i<k;i++)
- {
- scanf("%d%d%d",&d,&x,&y);
- if(x>n||y>n)
- {
- sum++;
- continue;
- }
- if(d==2&&x==y)
- {
- sum++;
- continue;
- }
- root1=find(x);
- root2=find(y);
- if(root1!=root2)//合并操作
- {
- s[root2].father=root1;
- s[root2].relation=(3+(d-1)+s[x].relation-s[y].relation)%3;//域的更新
- }
- else
- {
- if(d==1&&s[x].relation!=s[y].relation)
- {
- sum++;
- continue;
- }
- if(d==2&&((3-s[x].relation+s[y].relation)%3!=d-1))
- {
- sum++;
- continue;
- }
- }
- }
- printf("%d\n",sum);
- return 0;
- }
这是一种做法,看到挑战程序设计竞赛里面的有另一种做法。这里也可以用不带秩的合并(内存可以少一点)。
- #include<stdio.h>
- #include<string.h>
- #define MAX 10000
- int parent[MAX],rank[MAX];//父亲,树的高度
- int n,k,cnt;
- int find(int r)
- {
- int p=r;
- while(p!=parent[p]) p=parent[p];
- while(r!=p)
- {int temp=parent[r];parent[r]=p;r=temp; }
- return p;
- }
- void unionn(int r1,int r2)
- {
- r1=find(r1),r2=find(r2);
- if(rank[r1]<rank[r2])
- {parent[r1]=r2; }
- else
- {
- parent[r2]=r1;
- if(rank[r1]==rank[r2]) rank[r1]++;//因为r2的父节点变成了r1,深度+1
- }
- }
- bool same(int a,int b)
- {
- return find(a)==find(b); //相等表示同一个集合
- }
- int main()
- {
- while(~scanf("%d%d",&n,&k))
- {
- cnt=0;
- for(int i=0;i<=3*n;i++)//这里赋值到3*n,一开始没注意到,然后结果也不对
- {
- parent[i]=i;
- }
- memset(rank,0,sizeof(rank));
- for(int i=0;i<k;i++)
- {
- int d,r1,r2;
- scanf("%d%d%d",&d,&r1,&r2);
- if(r1>n||r2>n)
- { cnt++;continue; }
- /*以下要做的,就是r1表示A种类,r1+n表示B,r1+2*n表示C ,r2同理。
- */
- if(d==1)//同类
- {
- if(same(r1,r2+n)||same(r1,r2+2*n) )
- /*这里是处理同类的2种情况,
- 假如r1(A种类)和r2(B种类)是同一个集合,那么这肯定是假话
- 假如r1(A种类)和r2(C种类)是同一个集合,也是假话
- */
- cnt++;
- else
- {unionn(r1,r2);unionn(r1+n,r2+n);unionn(r1+2*n,r2+2*n); }
- /*假如是真话,它们就是同类,分别合成一个集合,A与A,B与B,C与C */
- }
- else//x吃y
- {
- if(same(r1,r2)||same(r1,r2+2*n) )
- /*这里是处理x吃y的2种情况,
- 假如r1(A种类)和r2(A种类)是同一个集合,不能同类相吃 ,是假话
- 假如r1(A种类)和r2(C种类)是同一个集合,也是假话 ,只有C种类吃A种类,A种类只能吃B种类。
- */
- cnt++;
- else
- {unionn(r1,r2+n);unionn(r1+n,r2+2*n);unionn(r1+2*n,r2); }
- /*假如是真话,那么就让x吃y的关系合成一个集合,A种类吃B,B吃C,C吃A */
- }
- }
- printf("%d\n",cnt);
- }
- return 0;
- }