POJ_1182

这个题目在看了别人的解法之后不由地感叹原来并查集也可以这么用。

首先,由于ABC三种生物之间存在着食物链的关系,如果单单用并查集p[]是解决不了问题了,这时还需要一个附加的数组r[]来表示xp[x]的关系,我选用的关系为0代表xp[x]同类,1代表xp[x]2代表p[x]x

之后难点一共有下面几处:

①并查集的查找和路径压缩。我们在压缩路径的时候必然要去找xfind(x)之间的关系,而xfind(x)有可能隔了很多层,因此我们需要从find(x)开始自底向上依次更新r[],有点类似深搜的回溯。在更新关系的时候运用到了类似向量的思考模式,比如我想知道ac的关系,而现在已知ab的关系,bc的关系,那么ac的关系就应该是ab的关系与bc的关系的“和”,回到这道题目,就是说在回溯的时候已知子节点和父节点的关系,以及父节点和根结点的关系,然后把两个关系“相加”就得到了子节点与根节点的关系。

②并查集的合并操作。我们比较容易理解,如果xy不属于一个并查集,那么这句话就一定是真话,但难点在于我们如何把这两个并查集进行合并,也就是说如何去找到find(x)find(y)之间的关系。这里同样要运用类似向量的思想,即我们现在已知xfind(x)的关系,yfind(y)的关系,以及xy的关系,3个量进行运算之后自然就可以得到find(x)find(y)之间的关系。

③对矛盾的判断。我们比较容易知道,只有当find(x)==find(y)的时候才可能出现矛盾,这时我们如何知道这句话是否会和前面的话矛盾呢?其实也就是去判断当前已知的xy的关系是否与前面的关系矛盾,而我们又本来就知道xfind(x)的关系以及yfind(y)之间的关系,自然可以推导出正确xy之间的关系,那么便只要把输入的关系和推理的关系进行比较就可以知道输入的关系是否成立了。

#include<string.h>
#include<stdio.h>
int p[50010],r[50010],N,K;
int find(int x)
{
int tx;
if(p[x]==x)
return x;
tx=find(p[x]);
r[x]=(r[p[x]]+r[x])%3;
return p[x]=tx;
}
int check(int D,int x,int y)
{
int i,j,tx,ty;
if(x>N||y>N)
return 0;
if(D==2&&x==y)
return 0;
tx=find(x);
ty=find(y);
if(tx==ty)
{
if((r[y]-r[x]+D+2)%3==0)
return 1;
return 0;
}
else
{
p[tx]=ty;
r[tx]=(r[y]-r[x]+D+2)%3;
return 1;
}
}
int main()
{
int i,j,k,num,x,y,D;
while(scanf("%d%d",&N,&K)==2)
{
for(i=1;i<=N;i++)
{
p[i]=i;
r[i]=0;
}
num=0;
for(i=0;i<K;i++)
{
scanf("%d%d%d",&D,&x,&y);
if(!check(D,x,y))
num++;
}
printf("%d\n",num);
}
return 0;
}


posted on 2011-09-23 18:14  Staginner  阅读(316)  评论(0编辑  收藏  举报