您的位置:首页 > 其它

POJ 1182 食物链【经典并查集应用】

2014-02-12 21:56 246 查看
由于水平有限,一开始并没有做出来,看了解题思路开始做,然后就有一堆bug.最后奇葩一点事scanf输入可以通过,而cin输入就超时,fuck.

食物链

Time Limit: 1000MSMemory Limit: 10000K
Total Submissions: 33805Accepted: 9820
Description

动物王国中有三类动物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),输出假话的总数。

Input

第一行是两个整数N和K,以一个空格分隔。

以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。

若D=1,则表示X和Y是同类。

若D=2,则表示X吃Y。

Output

只有一个整数,表示假话的数目。

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


Source

Noi 01

分析详细的博客:http://cavenkaka.iteye.com/blog/1489588

http://blog.sina.com.cn/s/blog_88705ca20100ugc2.html

代码清晰:/article/4679305.html

算法:并查集

如果你不知道什么是并查集,请看并查集资料:

http://blog.csdn.net/cfreezhan/article/details/8629871

如果你没有做过并查集题目,请看这两篇博客并查集入门题目及其详解:

POJ 2524 http://blog.csdn.net/cfreezhan/article/details/8765355 判断有多少个不同的连通分量

POJ 1611 http://blog.csdn.net/cfreezhan/article/details/8765299 判断与第一个学生在同一个连通分量的学生数。

思路:把确定了相对关系的节点放在同一棵树中

每个节点对应的 r[]值记录他与根节点的关系:

0:同类,

1:被父亲节点吃,

2: 吃父亲节点

每次输入一组数据 d, x, y判断是否超过 N 后,先通过find()函数找他们的根节点从而判断他们是否在同一棵树中。(也就是是否有确定的关系)

1.如果在同一棵树中find(x) == find(y):直接判断是否说谎。

1)如果 d ==1,那么 x 与 y 应该是同类,他们的r[]应该相等

如果不相等,则说谎数 +1

2)如果 d==2,那么 x 应该吃了 y,也就是 (r[x]+1)%3 == r[y]

如果不满足,则说谎数 +1

如何判断 x 吃了 y 是 (r[x]+1)%3 == r[y],请看下图:(PS:箭头方向指向被吃方)



2.如果不在同一棵树中:那么合并 x 与 y 分别所在的树。

合并树时要注意顺序,我这里是把 x 树的根当做主根,否则会

WA的很惨

注意:找父亲节点时,要不断更新 r[]的值。

这里有一个关系:如果 x 和y 为关系 r1, y 和 z 为关系 r2

那么 x 和z的关系就是 (r1+r2)%3

如何证明?

无非是3*3 = 9种情况而已

(a, b) 0:同类 、 1:a被b吃 、 2:a吃b

(x, y)
(y, z)
(x,z)
如何判断
0
0
0
0+0 = 0
0
1
1
0+1 = 1
0
2
2
0+2 = 2
1
0
1
1+0 = 1
1
1
2
1+1 = 2
1
2
0
(1+2)%3 = 0
2
0
2
2+0 = 2
2
1
0
(2+1)%3 = 0
2
2
1
(2+2)%3 = 1
关于合并时r[]值的更新

如果 d == 1则 x和y 是同类 ,那么 y 对 x 的关系是 0

如果 d == 2 则 x 吃了 y, 那么 y 对 x 的关系是 1, x 对 y 的关系是 2.

综上所述 ,无论 d为1 或者是为 2, y 对 x 的关系都是 d-1

定义 :fx 为 x 的根点, fy 为 y 的根节点

合并时,如果把 y 树合并到 x 树中

如何求 fy 对 fx 的r[]关系?

fy 对 y 的关系为 3-r[y]

y 对 x 的关系为 d-1

x 对 fx 的关系为 r[x]

所以 fy 对 fx 的关系是(3-r[y] + d-1 + r[x])%3

我的理解:

1.用rank表示x与father[x]的关系(吃,被吃,同种).

2.用如果两个节点的根节点相同,即在同一棵树上,那么就合并两棵树.

3.对于不能确定是说谎的句子,就认为是正确的,(疑罪从无原则).

4.合并两棵树,递归修改rank.

#include <iostream>
using namespace std;

int father[100000];
int myrank[100000];

int findFather(int x){
int t;
if(x!=father[x])
{
t = father[x];
father[x]= findFather(father[x]);
//更新x与father[X]的关系
myrank[x] = (myrank[x]+myrank[t])%3;
}
return father[x];
}

void make_union(int x,int y,int d){
int xFather = findFather(x);
int yFather = findFather(y);
father[xFather] = yFather;
myrank[xFather] = (3-myrank[x]+myrank[y]+d)%3;
}

void make_set(int n){
while (n){
father
=n;
myrank
=0;
--n;
}
}

int main()
{
int n,k;
int x,y,d;
int lie = 0;
cin>>n>>k;
make_set(n);
while ( k-- ){
scanf("%d%d%d",&d,&x,&y);
//cin>>d>>x>>y; // 超时
if ( (x>n || y>n)||(d==2 && x==y)) {
lie++;
continue;
}
int xf = findFather(x);
int yf = findFather(y);
if (xf!=yf ||(3+myrank[x]-myrank[y])%3==d-1 ) {
make_union(x,y,d-1);
continue;
}
lie++;
}
cout<<lie<<endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: