这道食物链实在让我纠结了好久。。。 看别人的代码都看了好久。这里涉及到一种并查集的处理方法——向量偏移。同时有位大牛告诉我这种方法的本质就是要体会一句话:“陈冠希是谢霆锋情人的情人...谢霆锋是男人,所以陈冠希也是男人。”
这道题不单纯是查找根节点和合并节点,在处理的同时还要记录节点之间的关系。
这个博客让我明白了,向量偏移的方法。有图有真像哦!!!
http://hi.baidu.com/tomspirit/blog/item/fb8c5bd1a4c9552e960a16e1.html
这是我最开始理解并查集看的文章,写的很形象。
http://www.cnblogs.com/ACShiryu/archive/2011/09/17/union.html
(1)(2)向量图
#include<stdio.h>
struct node
{
int parent;
int relation;
}p[50010];//每个节点记录了它的父亲节点和此节点对当前所在集合的根节点的关系
int find(int x)
{
int t;
if(p[x].parent==x) return x;
else
{
t=p[x].parent;//记录未更新时候的父亲节点
p[x].parent=find(t);//继续查找根节点
p[x].relation=(p[t].relation+p[x].relation)%3;//更新此节点对根节点的关系(1)
}
return p[x].parent;
}
void join(int x,int y,int r1,int r2,int d)
{
p[r1].parent=r2;//r1的父亲节点指向r2
p[r1].relation=(3+d-1+p[y].relation-p[x].relation)%3;//更新关系(2)
//d-1 是因为relation 0 1 2代表同类 吃 和被吃 而 题中是1同类 2吃
}
int main()
{
int n,k,i,num,r1,r2,d,x,y;
scanf("%d%d",&n,&k);
for(i=1;i<=n;i++)//初始化
{
p[i].parent=i;
p[i].relation=0;
}
num=0;
while(k--)
{
scanf("%d%d%d",&d,&x,&y);
if(x>n||y>n||(d==2&&x==y)) num++;
//如果编号大于最大编号或者x吃y并且x,y同类则此话为假话
else
{
r1=find(x);
r2=find(y);
if(r1!=r2)//如果根节点不同那么就合并
join(x,y,r1,r2,d);
else//如果根节点相同则判断x,y到根节点的关系进而判断是否是假话
{
if(d==1&&p[x].relation!=p[y].relation) num++;//(3)
else if(d==2&&(p[x].relation-p[y].relation+3)%3!=1) num++;//(4)
}
}
}
printf("%d",num);
}