并查集应用 —— POJ 1182 食物链
来源:互联网 发布:数控车床编程视频教程 编辑:程序博客网 时间:2024/05/16 17:56
对应 POJ 题目:点击打开链接
Description
现有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
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
Output
Sample Input
100 71 101 1 2 1 22 2 3 2 3 3 1 1 3 2 3 1 1 5 5
Sample Output
3
题意:中文题,不多说
思路:
开始时把同一类的动物并在相同的集合往死里想,做了一堆无用功;首先要正确理解的一点是:要把相互之间有关系(吃与被吃关系,同类关系)的,或者他们之间的关系可以通过集合所构成的树推导出来的动物(比如x, y)并在相同的集合
如上图,在这棵以 5 为根的树中,其中每个结点之间都存在关系,比如 1 和 2 之间,他们的关系要么是1 吃 2; 要么是 2 吃 1 ;要么1 和 2 是同类。确定他们之间关系的方法将重点讲。
我们用 fa[x] = y 表示 x 的父亲结点为 y;然后用一个rela[] 数组表示 x 与 fa[x] 的关系。
rela[x] = 0 表示 x 与 fa[x] 是同类,
rela[x] = 1 表示 x 吃 fa[x],
rela[x] = 2 表示 fa[x] 吃 x。
看这样一颗3个结点的树,
(图1) (图2)
假如此时rela[x] = 1;relax[y] = 1;relax[z] = 0(也只能等于0,因为它是根,自己跟自己是同类)
即是 x 吃 y ,y 吃 z ;由这两点再根据题目意思还可以知道 z 吃 x
当我们对 x 调用 fx = Find(x) 时,这时必须知道什么是并查集的路径压缩,不知道的先弄懂再往下看;
我们就会把(图1 )变成(图2),但此时rela[x] = 1 就错了(这样就是 x 吃 z ,与原意 z 吃 x 不符)
所以在压缩路径的时候要修改 rela[x] 的值,怎样修改?!
其实我们可以根据 x 对 y 的关系,以及 y 对 z 的关系,推导出 x 对 z 的关系。枚举这个过程就是:
rela[x] rela[y] rela[z] | 修改后的 rela[x]
0 0 0 0
0 1 0 1
0 2 0 2
1 0 0 1
1 1 0 2
1 2 0 0
2 0 0 2
2 1 0 0
2 2 0 1
通过枚举所有情况我们发现
修改后的 rela[x] = (rela[x] + rela[y]) % 3;
因此在令 z = fa[x] 之前加上这句:rela[x] = (rela[x] + rela[y]) % 3 就能正确描述结点之间的关系。
-------------------------------------------------------------------------------------------------------------------------------------------
这样对于一个操作 d x y;我们可以这样处理:
令 fx = Find(x); fy = Find(y); 如果 fx == fy ;即x 与 y 在同一个集合里。这好办,根据路径压缩我们会得到这样的一棵树:
即 x 与 y 都直接指向根结点(也就是集合的代表)。这时我们知道的是 x 对 root 的关系 rela[x] 和 y 对 root 的关系 rela[y];问题是我们要知道的是 x 对 y 的关系,因为我们要以此来判断 d x y 是否正确。
如果我们把上图想象成(只是想象,实际并不进行这样的操作):
即改变了root 与 y 的父子关系
rela[x] 没有变,我们不去动它;但 rela[root] 不再是 0 ,这里我直接说了,通过枚举我们可以知道 rela[root] = 3 - rela[y]。接下来有没有熟悉的感觉,就是上面(图1)的那个过程;这里就可以确定 x 与 y 的关系了。即 x 对 y 的关系 rxy = (rela[x] + rela[root]) % 3;展开就是 rxy = (rela[x] + (3 - rela[y])) % 3;
接下来如果 rxy != d - 1 的话,那 d x y 这句话就是假的(把 d = 1 和 d = 2 代入你会发现的确是这样)
如果 fx != fy 呢?这就说明x 与 y 不在同一个集合里面 。这更好办,说明 x 与 y 还没有任何关系,你怎样说都是对的。麻烦的是定义了x 与 y 的关系之后(比如 1 x y 或 2 x y),你要把 x 所在的集合跟 y 所在的集合合并,使得 x 与 y 存在关系。
我们通过 fx = Find(x) 和 fy = Find(y) 同样可以得到这样图:
为了方便观察我们可以简化这个图:
这里我们把 x 所在集合接到 y 所在集合里,即最终令 fa[fx] = fy,即
但在这之前我们要先确定 fx 对 fy 的关系,即修改 rela[fx]。怎样修改?我们通过 d x y 操作可以知道 x 对 y 的关系为 d - 1 (d = 1 那 d - 1 = 0 就表示 x 与 y 是同类,d = 2 那 d - 1 = 1 就表示 x 吃 y,跟我们的定义是一样的);即我们知道了 x 对 y 的关系,y 对 fy 的关系,那根据前面的公式, x 对 fy 的关系 x_fy = ((d - 1) + rela[y]) % 3。到这里我们可以想象到这样一个图:
这里我们知道 x 对 fx 的关系 rela[x] 和 x 对 fy 的关系 x_fy;那我们可以像前面那样改变 x 与 fx 的父子关系,使图变成这样:
相应地可以求 fx 对 x 的关系为 fx_x = 3 - rela[x];这样就可以求 fx 对 fy 的关系了!即 fx 对 fy 的关系 fx_fy = (fx_x + x_fy) % 3。把原数代入就是:
fx_fy = ((3 - rela[x]) + ((d - 1) + rela[y]) % 3) % 3 也就是:
fx_fy = (2 - rela[x] + d + rela[y]) % 3
这样就可以合并两个集合了。
必须叹服!这道题把并查集运用地非常巧妙!
#include <stdio.h>#include <stdlib.h>#include <string.h>#define N 50010int fa[N];int rela[N];void Init(){int i;for(i = 0; i < N; i++){fa[i] = i;rela[i] = 0;}}int Find(int x){int fx;if(x == fa[x]) return x;fx = Find(fa[x]);rela[x] = (rela[x] + rela[fa[x]]) % 3;fa[x] = fx;return fx;}void Union(int x, int y, int d) /* 使 x 所在集合成为 y 所在集合的子集合 */{int fx, fy;fx = fa[x];fy = fa[y];rela[fx] = ((3 - rela[x]) + (d - 1) + rela[y]) % 3;fa[fx] = fy;}int main(){#if 0freopen("in.txt","r",stdin);#endifint n, m, count = 0;scanf("%d%d", &n, &m);Init();while(m--){int d, x, y, fx, fy;scanf("%d%d%d", &d, &x, &y);if(x < 1 || x > n || y < 1 || y > n){count++;continue;}fx = Find(x);fy = Find(y);if(fx == fy){if((rela[x] + (3 - rela[y])) % 3 != d - 1) count++;}else Union(x, y, d);}printf("%d\n", count);return 0;}
- 并查集应用 —— POJ 1182 食物链
- 【poj 1182】食物链 并查集应用
- POJ 1182 食物链——种类并查集
- 并查集——POJ 1182 食物链
- POJ 1182 食物链——并查集
- poj 1182 食物链 —— 种类并查集
- POJ 1182 食物链【经典并查集应用】
- POJ 1182 食物链【经典并查集应用】
- POJ-1182-食物链- 经典并查集应用
- POJ 1182 食物链 (并查集扩展应用)
- poj 1182 食物链(并查集的综合应用)
- POJ 1182 食物链【经典并查集应用】
- poj 1182 食物链(并查集)
- POJ 1182-食物链 并查集
- POJ 1182 食物链 并查集
- poj 1182 食物链 并查集
- POJ 1182 食物链 并查集
- poj 1182 --食物链(经典并查集)
- DES加密解密
- 下拉刷新,上拉加载 随笔。
- HTML 标签学习
- mybatis存储过程调用
- NSURLConnection提供了异步请求、同步请求两种通信方式
- 并查集应用 —— POJ 1182 食物链
- jenkins maven tomcat做持续集成的时候几个关键配置
- 入侵指定网站的思路~菜鸟必学
- css浮动
- 【Windows10】如何使用Segoe MDL2 Assets图标
- 排列、R子集字典序
- Cocos2d-JS项目之三:使用合图
- android 使用AndroidAnnotations注解简化安卓开发
- (79)最长公共子串