POJ 1182 食物链(带权并查集)
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 65579 | Accepted: 19336 |
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 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
思路
解法一:来自《挑战程序设计竞赛》
由于N和K很大,所以必须高效地维护动物之间的关系,并快速判断是否产生了矛盾。并查集是维护 “属于同一组” 的数据结构,但是在本题中,并不只有属于同一类的信息,还有捕食关系的存在。因此需要开动脑筋维护这些关系。
对于每只动物i创建3个元素i-A, i-B, i-C, 并用这3*N个元素建立并查集。这个并查集维护如下信息:
- i-x 表示 “i属于种类x”。
- 并查集里的每一个组表示组内所有元素代表的情况都同时发生或不发生。
例如,如果i-A和j-B在同一个组里,就表示如果i属于种类A那么j一定属于种类B,如果j属于种类B那么i一定属于种类A。因此,对于每一条信息,只需要按照下面进行操作就可以了。
- 第一种,x和y属于同一种类———合并x-A和y-A、x-B和y-B、x-C和y-C。
- 第二种,x吃y—————————合并x-A和y-B、x-B和y-C、x-C和y-A。
不过在合并之前需要先判断合并是否会产生矛盾。例如在第一种信息的情况下,需要检查比如x-A和y-B或者y-C是否在同一组等信息。
解法二:加权并查集
思路来源于:click here
Part I - 权值(relation)的确定。
注:每个节点对应的 relation[]值记录他与根节点的关系:
我们根据题意,森林中有3种动物。A吃B,B吃C,C吃A。
我们还要使用并查集,那么,我们就以动物之间的关系来作为并查集每个节点的权值。
注意,我们不知道所给的动物(题目说了,输入只给编号)所属的种类。
所以,我们可以用动物之间“相对”的关系来确定一个并查集。
- 0 - 这个节点与它的父节点是同类
- 1 - 这个节点被它的父节点吃
- 2 - 这个节点吃它的父节点
注意,这个0,1,2所代表的意义不是随便制定的,我们看题目中的要求。
输入的时候,第一个数字(下文中,设为d)指定了后面两种动物的关系:
- 1 - X与Y同类
- 2 - X吃Y
我们注意到:
- 如果d == 1 则 x 和 y 是同类 ,那么 y 对 x 的关系是 0
- 如果d == 2 则 x 吃了 y, 那么 y 对 x 的关系是 1, x 对 y 的关系是 2.
- 综上所述 ,无论 d为1 或者是为 2, y 对 x 的关系都是 d-1
所以,这个0,1,2不是随便选的
Part II - 路径压缩,以及节点间关系确定
确定了权值之后,我们要确定有关的操作。
我们把所有的动物全初始化 relation[i]=0,f[i]=i
(1)路径压缩时的节点算法
通过穷举我们可以发现
f[now]=f[f[now]]
relation[now]=(relation[now]+relation[f[now]]) % 3
这个路径压缩算法是正确的
关于这个路径压缩算法,还有一点需要注意的地方,我们一会再谈
注意,根据当前节点的relation和当前节点父节点的relation推出
当前节点与其父节点的父节点的relation这个公式十分重要!!
它推不出来下面都理解不了!!自己用穷举法推一下:
好吧,为了方便伸手党,我给出穷举过程
i j
爷爷 父亲 儿子 儿子与爷爷
0 0 (i + j)%3 = 0
0 1 (i + j)%3 = 1
0 2 (i + j)%3 = 2
1 0 (i + j)%3 = 1
1 1 (i + j)%3 = 2
1 2 (i + j)%3 = 0
2 0 (i + j)%3 = 2
2 1 (i + j)%3 = 0
2 2 (i + j)%3 = 1
这样可以看到,( 儿子relation + 父亲relation ) % 3 = 儿子对爷爷的relation
所以有relation[now]=(relation[now]+relation[f[now]]) % 3
这就是路径压缩的节点算法
(2) 集合间关系的确定
当确定D X Y 的关系的正确性后,要把二者的集合合并为一个集合。就是把Y的集合的根节点其父亲设置为X的集合的根节点。
y的根节点与x的根节点间的关系(即fy与fx的关系):
relation[find(y)]=(3-relation[y]+(d-1)+relation[x]) % 3;
这个公式,是分三部分,这么推出来的:
( d - 1 ) :这是X和Y之间的relation,X是Y的父节点时,Y的relation就是这个
3 - relation[y] = 根据Y与根节点的关系,逆推根节点与Y的关系
这部分也是穷举法推出来的,我们举例:
0(父子同类)( 3 - 0 ) % 3 = 0
1(父吃子) ( 3 - 1 ) % 3 = 2 //父吃子
2(子吃父) ( 3 - 2 ) % 3 = 1 //子吃父
所以有y的父结点与x的父结点关系relation[find(y)]=(3-relation[y]+(d-1)+relation[x]) % 3;
注意,这个当所有集合都是初始化状态的时候也适用
Part III - 判断
每次输入一组数据 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,证明过程上面已经论证过了。
#include<stdio.h> #include<string.h> const int maxn = 50005; int N,fa[maxn*3]; int find(int x) { int r = x; while (r != fa[r]) r = fa[r]; int i = x,j; while (i != r) { j = fa[i]; fa[i] = r; i = j; } return r; } void unite(int x,int y) { x = find(x),y = find(y); if (x != y) fa[x] = y; } bool same(int x,int y) { return find(x) == find(y); } int main() { int K,D,X,Y,res = 0; scanf("%d%d",&N,&K); for (int i = 1;i <= 3*N;i++) fa[i] = i; while (K--) { scanf("%d%d%d",&D,&X,&Y); if (X < 1 || X > N || Y < 1 || Y > N) { res++; continue; } if (D == 1) { if (same(X,Y+N) || same(X,Y+2*N)) res++; else unite(X,Y),unite(X+N,Y+N),unite(X+2*N,Y+2*N); } else if (D == 2) { if (same(X,Y) || same(X,Y+2*N)) res++; else unite(X,Y+N),unite(X+N,Y+2*N),unite(X+2*N,Y); } } printf("%d\n",res); return 0; }
#include<stdio.h> const int maxn = 50005; int r[maxn],fa[maxn]; int find(int x) { if (x == fa[x]) return fa[x]; int tmp = fa[x]; fa[x] = find(fa[x]); r[x] = (r[x] + r[tmp])%3; return fa[x]; } void unite(int x,int y,int d) { int fx = find(x),fy = find(y); fa[fy] = fx; //y被x吃,所以以x作为y的根节点 r[fy] = (r[x] - r[y] + 3 + (d - 1))%3; } int main() { int N,K,X,Y,D,res = 0; scanf("%d%d",&N,&K); for (int i = 1;i <= N;i++) r[i] = 0,fa[i] = i; while (K--) { scanf("%d%d%d",&D,&X,&Y); if (X > N || Y > N ||(X == Y && D == 2)) res++; else if (find(X) == find(Y)) { if (D == 1 && r[X] != r[Y]) res++; if (D == 2 && (r[X] + 1) % 3 != r[Y]) res++; } else unite(X,Y,D); } printf("%d\n",res); return 0; }
POJ 1182 食物链(带权并查集)的更多相关文章
- poj 1182 食物链 带权并查集
食物链是并查集的进阶运用的一道非常经典的题目. 题目如下: 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A, ...
- POJ 1182 食物链 (带权并查集 && 向量偏移)
题意 : 中文题就不说题意了…… 分析 : 通过普通并查集的整理归类, 能够单纯地知道某些元素是否在同一个集合内.但是题目不仅只有种类之分, 还有种类之间的关系, 即同类以及吃与被吃, 而且重点是题目 ...
- K - Find them, Catch them POJ - 1703 (带权并查集)
题目链接: K - Find them, Catch them POJ - 1703 题目大意:警方决定捣毁两大犯罪团伙:龙帮和蛇帮,显然一个帮派至少有一人.该城有N个罪犯,编号从1至N(N<= ...
- POJ - 2912 Rochambeau (带权并查集+枚举)
题意:有N个人被分为了三组,其中有一个人是开了挂的.同组的人的关系是‘=’,不同组的人关系是‘<’或'>',但是开了挂的人可以给出自己和他人任意的关系.现在要根据M条关系找出这个开了挂的人 ...
- A Bug's Life POJ - 2492 (带权并查集)
A Bug's Life POJ - 2492 Background Professor Hopper is researching the sexual behavior of a rare spe ...
- poj1182 食物链 带权并查集
题目传送门 题目大意:大家都懂. 思路: 今天给实验室的学弟学妹们讲的带权并查集,本来不想细讲的,但是被学弟学妹们的态度感动了,所以写了一下这个博客,思想在今天白天已经讲过了,所以直接上代码. 首先, ...
- poj 1182:食物链(种类并查集,食物链问题)
食物链 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 44168 Accepted: 12878 Description ...
- POJ 1182 食物链(种类并查集)
食物链 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 63592 Accepted: 18670 Description ...
- poj 1182 食物链(关系并查集)
食物链 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 62824 Accepted: 18432 Description ...
- POJ 1182 食物链 (种类并查集)
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种.有人用两种说 ...
随机推荐
- devexpress xtrareport 并列绑定两个数据源,如何实现?
如下图,要在xtrareport 并列绑定两个不同的数据源datatable1和datatable 2,并且table1中的只有10行数据,table2中有20行数据,如何实现
- es6+移动轮播插件
前言:之前赶项目,都是直接用框架,对于touch事件是模拟两可,趁着有心情,用es6写一个原生移动轮播插件. 用了es6的新特性,确实挺爽的,说到es6,就不得不说到babel,博主已经码好了,直接用 ...
- Apache POI 实现对 Excel 文件读写
1. Apache POI 简介 Apache POI是Apache软件基金会的开放源码函式库. 提供API给Java应用程序对Microsoft Office格式档案读和写的功能. 老外起名字总是很 ...
- SQL基础之XML
1.XML数据类型 在SQL Server中xml数据类型可以用来保存xml文档,这个文档即可以是完整的xml文档和xml片段,这样开发者就可以像使用int数据类型一样来使用xml数据类型.不过xml ...
- 仿各种APP将文章DOM转JSON并在APP中以列表显示(android、ios、php已开源)
背景 一直以来都想实现类似新闻客户端.鲜城等文章型app的正文显示,即在web editor下编辑后存为json,在app中解析json并显示正文. 网上搜过,没找到轮子.都是给的思路,然后告知是公司 ...
- SQL Server使用游标或临时表遍历数据
方法一:使用游标(此方法适用所有情况,对标结构没有特殊要求.) declare @ProductName nvarchar() declare pcurr cursor for select Prod ...
- MPLS
Multiprotocol Label Switching From Wikipedia, the free encyclopedia "MPLS" redirects here. ...
- Form表单提交的简要方式
<html> <head> <meta name="viewport" content="width=device-width" ...
- Service之来电监听(失败的案例)
Service:服务,可以理解成一个运行再后台没有界面的Activity,集成于Seriver,是四大组件之一 Service的继承关系:Service-->ContextWrapper--&g ...
- Map集合的应用及其遍历方式
---> HashMap :底层基于哈希表 存储原理也使用哈希表来存放的: 往HashMap添加了元素 ,首先会调用键的hashCode方法 获得一个哈希值,然后 ...