Poj1182食物链 (并查集)
来源:互联网 发布:阿里云授权服务中心 编辑:程序博客网 时间:2024/04/18 16:35
题目链接:
http://poj.org/problem?id=1182
题意:
最多n个动物,每个都有可能是A,B,C三种中的一种,k句话,两种类型:
1.1 x y 代表x与y是用一种
2.2 x y 代表x吃y
输出谎话的数目。
两种做法都是并查集:
第一种:因为每个动物都是这三种的一种,所以为每个动物初始化三种可能x-A x-B x-C分别代表x这个动物属于A,B,C种。用并查集来维护这些关系,在同一个集合中的元素具有相同的真假性质
对于1 x y:检查x与y+n是否在同一个集合和x与y+2n是否在同一个集合 如果有一个为真,表明x与y不是同类,更新结果,否则分别合并(x,y)(x+n,y+n)(x+2n,y+2n),表明x与y要么都属于A要么都属于B要么都属于C。
对于2 x y:检查x与y是否在同一个集合和x与y+2n是否在同一个集合 如果一个为真,表明x与y不是x吃y的关系,更新结果,否则分别合并(x,y+n)(x+n,y+2n)(x+2n,y),表明x吃y,要么为AB要么为BC要么为CA。
#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int maxn = 50000*3+5;int fa[maxn],height[maxn];//fa[i]=i 代表i属于A种类 fa[i+n]代表i属于B种类 fa[i+2n]代表i属于C种类void ini(int n){ for(int i=1;i<=3*n;i++) { fa[i]=i; height[i]=0; }}int findfa(int x){ if(fa[x]==x) return x; return fa[x]=findfa(fa[x]);}void unite(int x,int y){ x=findfa(x); y=findfa(y); if(x==y) return ; if(height[x]<height[y]) fa[x]=y; else { fa[y]=x; if(height[x]==height[y]) height[x]++; }}bool same(int x,int y){ if(findfa(x)==findfa(y)) return true; return false;}int main(){ //freopen("in.txt","r",stdin); int n,k,res; scanf("%d%d",&n,&k); { res=0; ini(n); for(int i=1;i<=k;i++) { int d,x,y; scanf("%d%d%d",&d,&x,&y); if(x>n||y>n||x<=0||y<=0) {res++;continue;} if(d==1) {//x与y同种 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 {//x吃y if(same(x,y)||same(x,y+2*n)) //如果x在A组且y在A组 或者 x在A组y在C组 则矛盾 res++; else { unite(x,y+n); unite(x+n,y+2*n); unite(x+2*n,y); } } } cout<<res<<endl; } return 0;}
第二种:带权并查集。增添一个数组r[i]代表i与其父节点之间的关系,定义0为同类,1为i被fa[i]吃,2为i吃fa[i],关键就是如何维护r[i]数组了。如果两个动物不在一个集合中,表示他们之间的关系还没有定义,直接进行合并操作。如果我们知道1->2和2->3的关系,如何推出1->3的关系,通过列表归纳(看别人题解- -)可以得到如果一个点为a,父节点为fa,爷爷节点为ffa,那么a->ffa=(a->fa+fa->ffa)%3即 r[a]=(r[a]+r[fa])%3,有了这个关系,合并集合的时候就可以推导出那个关系了。具体看代码。
#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int maxn = 50005;int fa[maxn],r[maxn];//fa[i]代表i的父节点 r[i]代表i与父节点的关系 0同种 1被父节点吃 2吃父节点void ini(int n){ for(int i=1;i<=n;i++) { fa[i]=i; r[i]=0;//自己与自己是同种 }}int findfa(int a) //返回值为a的父节点{ if(fa[a]==a) return a; int faa=fa[a]; //每次对fa[a]递归前先把它记录下来 因为回溯过程中fa[a]已经路径压缩 而递推关系的时候又需要fa[a] fa[a]=findfa(fa[a]); r[a]=(r[faa]+r[a])%3; //递推关系 return fa[a];}void unite(int a,int b,int d) //合并a与b的关系{ int faa=findfa(a),fab=findfa(b);//得到的过程中已经进行了路径压缩 即r[a]就是a与它的树根的关系 r[b]就是b与它的树根的关系 if(faa==fab) return ; fa[fab]=faa; r[fab]=(r[a]+3-r[b]+d-1)%3; //递推出新的关系}int main(){ //freopen("in.txt","r",stdin); int n,k,res; scanf("%d%d",&n,&k); ini(n); res=0; for(int i=1;i<=k;i++) { int d,x,y; scanf("%d%d%d",&d,&x,&y); if(x>n||y>n||x<=0||y<=0) {res++;continue;} if(d==2&&x==y) {res++;continue;} int faa=findfa(x),fab=findfa(y); if(d==1&&faa==fab&&r[x]==r[y]) continue; //在一个关系树中且是同一类 if(d==2&&faa==fab&&(1+r[x])%3==r[y]) continue; //在一个关系树中且是x吃y的关系 if(faa!=fab) //若不在一个关系树中 则不可能出现矛盾 合并关系树 推导出正确的关系 unite(x,y,d); else res++; //不符合上面的所有情况则发生了矛盾 } cout<<res<<endl; return 0;}并查集还是很神奇滴~
- 食物链 POJ1182 -- 并查集
- poj1182 食物链 (并查集)
- poj1182 并查集 食物链
- poj1182食物链 并查集
- 并查集-POJ1182食物链
- POJ1182 - 食物链 - 并查集
- 并查集 食物链 POJ1182
- poj1182 食物链(并查集)
- POJ1182 食物链 并查集
- Poj1182食物链 (并查集)
- POJ1182 食物链(并查集)
- 【并查集】:poj1182,食物链
- POJ1182 食物链(并查集)
- POJ1182 食物链 并查集
- poj1182 食物链[并查集]
- poj1182 食物链(并查集)
- POJ1182 食物链【并查集】
- poj1182食物链(种类并查集)
- 如何更改linux文件的拥有者及用户组(chown和chgrp)
- UIVIew的使用大全
- 数据挖掘回顾十一:关联规则挖掘之 Apirori 算法
- 堆和栈的区别(转过无数次的文章)
- nyoj76超级台阶 fibonacci数
- Poj1182食物链 (并查集)
- VxWorks的移植和BSP定制过程
- 文件操作之fread()和fwrite()函数
- 关于内存函数GlobalLock(),GlobalAlloc(),GlobalUnLock()用法
- 入职第一天总结----web.xml配置说明
- 地图坐标查询api
- Unity3D研究院之两种方式播放游戏视频
- 几条 ffmpeg 的命令
- 利用opencv2和c++实现视频与图像的互相转换