食物链 (种类并查集(裸))

来源:互联网 发布:我家门前有两棵树 知乎 编辑:程序博客网 时间: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);}
0 0
原创粉丝点击