食物链 (种类并查集(裸))
来源:互联网 发布:我家门前有两棵树 知乎 编辑:程序博客网 时间:2024/05/22 04:54
题目来源:https://vjudge.net/problem/POJ-1182
【题意】
汉语的话,题意应该都看得懂。
【思路】
先附上大佬两行网址,以表敬意。
一:http://blog.csdn.net/libing923/article/details/8240995/
二:http://www.cnblogs.com/dongsheng/archive/2013/06/12/3133188.html
然后总结自己的:(一个小习惯)
给出一组样例:
5 4
2 1 2(第一行)
2 2 3(第二行)
2 4 5(第三行)
2 2 4(第四行)
起初我看这道题的时候,误以为是找当前节点与其父节点的关系。所以,就根据我栽的跟头讲一下我对这道题的理解。(可以边看代码边看我的理解,不然小心看不懂 (—。—))
pre[x]表示x根结点。relax[x]表示pre[x]与x关系。relax[x]=0 表示pre[x]与x同类;1表示pre[x]吃x;2表示x吃pre[x]。
为了看懂代码,我开始输出中间过程。假设,pre数组存的是各自对应的根节点,而relax数组存的是当前节点与根节点的关系,那么我就在代码对relax数组进行操作的时候,输出relax数组的值(请自动找到注释区域),假设我们现在运行的是上面提到的那一组样例。
运行结果是:(relax数组)
one 0 0 0 0 0 two 0 1 0 0 0
one 0 1 0 0 0 two 0 1 2 0 0
one 0 1 2 0 0 two 0 1 2 0 1
one 0 1 2 0 1 two 0 1 2 2 1
那么现在我们分析一下,当第一行输入后,relax[2]变成了1,根据题意,表明是1吃2,接着,输入第二组数据,relax[3]变成了2,代码运行里具体操作是这样的,因为2和3的根节点不一样,所以不能够判定是对是错,接着,就要把2的根节点作为3的根节点(本身)的父亲节点,relax[3]=2代表他吃根节点,这是为什么呢,因为题上说了,只有三类动物,A吃B,B吃C,C吃A,接着输入第三行数据,4和5的根节点不一样,把4当做是5的老爹,所以relax[5]=1,relax[4]=0;此时4,5和1,2,3并没有什么关系,直到输入 第四行数据,2,4,找2的根节点是1,4的根节点是4,不相等,所以就把1当成4的老爹,而relax【4】=2,他和3是一类,但是这个时候发现一个问题,一个很大的问题:
节点4的relax是更新了,但是relax【5】记录却还是5和先前的根节点的也就是4的关系,而不是现在的根节点1的关系,我一直在想他会在哪里进行改变,结果发现咋在find函数里有一句:
int fx=find(pre[x]);
relax[x]=(relax[x]+relax[pre[x]])%3;
return pre[x]=fx;
这里有我来给模拟一下这个递归的过程(第四组数据输入的时候(2,4)):
因为2的父亲节点不是本身,所以进入递归,fx=find(pre【x】)。当前find函数的形参是pre[x],而pre[x]=1,并且pre[pre[x]]=1,也就是1的祖先就是本事,所以x=pre[x],递归返回,fx=1,relax[2]=(relax[2]+relax[pre[x]])%3,所以,relax[2]依旧等于1,只要他的根节点不变,relax数组指的是当前点与根节点的关系,然后,对4进行查找,4的节点是4,所以,不变,然后MIX函数里,把2的根节点赋给4当祖先,relax[fc]=(relax[b]-relax[c]+a+3)%3;也就是relax[4]=(relax[2]-relax[4]+1+3)%3=2;
因为我之前输出中间过程的时候,发现它只更新了4的根节点,5的relax依旧是相对于4来说的,所以relax[5]依旧等于1,怎么对他进行更新呢?
仔细看:
假如我此时再原来的基础上再输入一行 2 5 6
那么此时的relax数组是这样的:(另外,把n的值变为6,k的值变为5)
one 0 1 2 2 0 0
two 0 1 2 2 0 1
这个时候,你会发现本来应该是relax[5]=1的在one就变成了0,那么这个更新的操作一定是在find函数里,那么我来模拟一下,int fb=find(b);进入递归,因为b的值是5,pre【5】=4;所以第一层递归形参是4,接着再进去递归(因为执行不到后面的),第二层递归形参是1,然后经过判断,1=pre【1】,所以递归返回,先回到第一层递归,执行下面的操作,relax【4】=(relax【4】+relax【1】)%3,因为relax【1】=0,所以不变,节点返回到顶层,relax【5】=(relax【5】+relax【4】)%3;得0,且pre【5】=1,这一步进行了更新。
好了,长篇大论结束了,总结一下,判断他们的关系有没有错,至于要把他们弄到一棵树上,以对根节点的关系作为分类的依据,更新。
这就是一道种类并查集经典题。
我解释题的方式偏重于思维,所以请带着我的思维去看大佬们的代码讲解,就可以在很短的时间内掌握这道题,拿走不谢。
#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<iostream>#include<map>#include<queue>#include<stack>#include<set>using namespace std;typedef long long LL;int pre[50000+10],relax[50000+10];int n,k;int find(int x){ if(x==pre[x]) return pre[x]; else { int fx=find(pre[x]); relax[x]=(relax[x]+relax[pre[x]])%3; return pre[x]=fx; }}bool MIX(int a,int b,int c){ int fb=find(b); int fc=find(c); if(fb==fc) { if((relax[c]-relax[b]+3)%3!=a) return 1; else return 0; }// printf("one ");// for(int i=1; i<=n; i++)// printf("%d ",relax[i]);// printf("\n"); pre[fc]=fb; relax[fc]=(relax[b]-relax[c]+a+3)%3;// printf("two ");// for(int i=1; i<=n; i++)// printf("%d ",relax[i]);// printf("\n"); return 0;}int main(){ int ans=0; scanf("%d%d",&n,&k); for(int i=1; i<=n; i++) pre[i]=i,relax[i]=0; while(k--) { int a,b,c; scanf("%d%d%d",&a,&b,&c); if(b>n||c>n||a==2&&b==c) { ans++; continue; } if(MIX(a-1,b,c)) ans++; } printf("%d\n",ans);}
- poj1182 食物链 (种类并查集)
- POJ1182:食物链(种类并查集)
- 食物链 (种类并查集(裸))
- poj 食物链(种类并查集)(思路)
- poj 1182 食物链(经典!种类并查集)
- POJ 1182 食物链 (种类并查集)
- poj 1182 食物链 (种类并查集经典题)
- POJ 1182 食物链 (三态种类并查集)
- POJ-1182 食物链 (种类并查集)
- POJ 1182 食物链 (种类并查集)
- poj 1182 食物链 && nyoj 207(种类并查集)
- POJ 1182 食物链(种类并查集 + 偏移量)
- POJ - 1182 食物链(种类并查集经典题)
- POJ1182食物链,HDU1829(种类并查集)
- POJ 1182 食物链(种类并查集)
- POJ1182 食物链 种类并查集(经典)
- POJ 1185 食物链(种类并查集)
- poj 1182 食物链(经典种类并查集)
- 【转】在C#中隐藏主窗口的方法
- C# 中如何获得屏幕宽度和高度
- 多线程
- mysql 1055 group by的错误
- C#播放背景音乐常用的四种方式
- 食物链 (种类并查集(裸))
- oracal清除用户下所有对象
- 黄易比较好的小说
- shell--if条件判断
- Binary XML file line #7: Error inflating class android.support.v7.widget.RecyclerView
- 关于StreamReader的ReadLine中Trim的使用
- 多线程webservie处理大量数据
- UPSPL清单 ALV输出
- C#中子线程修改主线程中textBox的内容