您的位置:首页 > 其它

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

2017-04-17 15:15 288 查看
题目来源: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
4000
是一类,但是这个时候发现一个问题,一个很大的问题:

节点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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: