POJ 1182 食物链 【并查集】

解题思路:首先是没有思路的----然后看了几篇解题报告

http://blog.csdn.net/ditian1027/article/details/20804911

http://poj.org/showmessage?message_id=152847

http://blog.163.com/jiazheng2222%40126/blog/static/16963238320101258935104/

这是discuss里面的一篇分析---

 http://poj.org/showmessage?message_id=152847
 我的理解是,对于集合里的任意两个元素a,b而言,它们之间必定存在着某种联系,
>
> 因为并查集中的元素均是有联系的,否则也不会被合并到当前集合中。那么我们
>
> 就把这2个元素之间的关系量转化为一个偏移量,以食物链的关系而言,不妨假设
>
> a->b 偏移量0时 a和b同类
>
> a->b 偏移量1时 a吃b
>
> a->b 偏移量2时 a被b吃,也就是b吃a
>
> 有了这些基础,我们就可以在并查集中完成任意两个元素之间的关系转换了。
>
> 不妨继续假设,a的当前集合根节点aa,b的当前集合根节点bb,a->b的偏移值为d-1(题中给出的询问已知条件)
>
> (1)如果aa和bb不相同,那么我们把bb合并到aa上,并且更新delta[bb]值(delta[i]表示i的当前集合根节点到i的偏移量)
>
>     此时 aa->bb = aa->a + a->b + b->bb,可能这一步就是所谓向量思维模式吧
>
>     上式进一步转化为:aa->bb = (delta[a]+d-1+3-delta[b])%3 = delta[bb],(模3是保证偏移量取值始终在[0,2]间)
>
> (2)如果aa和bb相同,那么我们就验证a->b之间的偏移量是否与题中给出的d-1一致
>
>     此时 a->b = a->aa + aa->b = a->aa + bb->b,
>
>     上式进一步转化为:a->b = (3-delta[a]+delta[b])%3,
>
>     若一致则为真,否则为假。
>
> 希望可以对LS有所帮助 :]

  

下面是自己的体会

pre[x]:表示x的父节点为pre[x]
p=find(x),其中p表示x的根节点
relation[x] :表示节点x与其根节点的关系,
             relation[x]=p->x(因为后面要用到向量,所以先把向量的方向说明出来,relation[x]向量代表从根节点p指向x的向量)
             relation[x]=0 表示p与x同类
             relation[x]=1 表示p吃x
			 relation[x]=2 表示x吃p
-----------------------------------------------------------------------------------
对于给定的一句话 d,x,y
 先判断x,y是否在同一个集合关系(集合是按照能够判断x,y关系来划分的)
 if(x,y在同一个集合)
 {
 	判断说的这句话的真假 ;
 }
 else
 {
 	这句话认为为真,将x,y合并起来;
 }
-----------------------------------------------------------------------------------
 判断一句话的 真假可以有两种办法
 (方法一)直接列出所有为真的情况,如果这句话不符合列举出的所有情况,则这句话为假
 1)
 d==1 表示x,y为同类
 relation[x]    relation[y]
    0               0
    1               1
    2               2
所以如果relation[x]!=relation[y],则这句话为假话
2)
d==2 表示x吃y
relation[x]    relation[y]
    0               2     //此时x与根节点同类,要让x吃y,relation[y]=2
    2               1     //此时x吃根节点,要让x吃y,则y与根节点同类,relation[y]=1
    1               0     //此时x被根节点吃,要让x吃y,则y应该吃根节点,relation[y]=1
如果不满足这三组对应的 值,则 这句话为假

(方法二)
详细见 http://poj.org/showmessage?message_id=152847
按照向量来做
x->y=x->p+p->y//因为此时p=q,所以可以将p换成q
    =-relation[x]+relation [y]
    =relation[y]-relation[x]//再进一步处理,为了防止为表达式的值为负数,给它加上3,
	=(relation[y]-relation[x]+3)%3 // 为使表达式的值在0到2之间,给表达式模 上3
又因为题目中给的是 d==1 x,y为同类,对应于我们规定的同类为0,应该将d-1
所以判断我们计算出的偏移量和说的那句话的偏移量是否一致,如果不一致,则说的 假话
即
((relation[y]-relation[x]+3)%3) !=d-1,则这句话为假话

---------------------------------------------------------------------------------------
  p,q不同时  合并 x,y
将q合并到p上,同时更新relation[q]
p->q=p->x+x->y+y->q
    =relation[x]+(d-1) +(-relation[y])//同样为防止表达式的值为负,加上3
    =(relation[x]-relation[y]+(d-1)+3) %3//为了使表达式的值在0到2之间,模上3
即 relation[q]=(relation[x]-relation[y]+(d-1)+3) %3

----------------------------------------------------------------------------------------
压缩路径的时候,同时更新relation[]数组
详细见:http://blog.csdn.net/ditian1027/article/details/20804911
现在知道儿子节点x, 父亲节点 fx,爷爷节点ffx,要求ffx->x
则 ffx->x=ffx->fx+fx->x
         =relation[fx]+relation[x]

即 relation[x]=(relation[x]+relation[tmp])%3
将fx记作x的亲生父亲,又因为find()函数是带路径压缩的,经过压缩后,x的父亲节点变为fx‘,不能用来计算
而我们需要的是fx的值 ,所以用tmp将它记录下来,再压缩
----------------------------------------------------------------------------------------

  反思:用向量表示relation[]数组时,向量起点和向量的终点一定要搞清楚,要不然后面的式子符号就会不对

下面是两种不同判断说话真假的代码

#include<stdio.h>
#define maxn 50010
int pre[maxn],relation[maxn];
int find(int a)
{
	int tmp;
	tmp=pre[a];
	if(a!=pre[a])
	pre[a]=find(pre[a]);
	relation[a]=(relation[a]+relation[tmp])%3;
	return pre[a];
}

void unionroot(int x,int y,int d)
{
	int p,q;
	p=find(x);
	q=find(y);
	pre[q]=p;
	relation[q]=(relation[x]-relation[y]+3+d-1)%3;
}

int main()
{
	int i,n,k,sum,x,y,p,q,d;
	scanf("%d %d",&n,&k);
		for(i=0;i<=maxn;i++)
		{
			pre[i]=i;
			relation[i]=0;
		}
		sum=0;
		while(k--)
		{
			scanf("%d %d %d",&d,&x,&y);
			p=find(x);
			q=find(y);
			if((x>n||y>n)||(x==y&&d==2))
			{
			sum++;
			continue;
		    }
			if(p==q)
			{
				if((relation[y]-relation[x]+3)%3!=d-1)
					 sum++;
			}
				else
				unionroot(x,y,d);
		}
		printf("%d\n",sum);
}

  

#include<stdio.h>
#define maxn 50010
int pre[maxn],relation[maxn];
int find(int a)
{
	int tmp;
	tmp=pre[a];
	if(a!=pre[a])
	pre[a]=find(pre[a]);
	relation[a]=(relation[a]+relation[tmp])%3;
	return pre[a];
}

void unionroot(int x,int y,int d)
{
	int p,q;
	p=find(x);
	q=find(y);
	pre[p]=q;
	relation[p]=(relation[y]-relation[x]+3+d-1)%3;
}

int main()
{
	int i,n,k,sum,x,y,p,q,d;
	scanf("%d %d",&n,&k);
		for(i=0;i<=maxn;i++)
		{
			pre[i]=i;
			relation[i]=0;
		}
		sum=0;
		while(k--)
		{
			scanf("%d %d %d",&d,&x,&y);
			p=find(x);
			q=find(y);
			if((x>n||y>n)||(x==y&&d==2))
			{
			sum++;
			continue;
		    }
			if(p==q)
			{
				if(d==1&&relation[x]!=relation[y])
				++sum;
				if(d==2)
				{
					if(relation[x]==0&&relation[y]!=2)
					++sum;
					if(relation[x]==1&&relation[y]!=0)
					++sum;
					if(relation[x]==2&&relation[y]!=1)
					++sum;
				}

			}
				else
				unionroot(x,y,d);
		}
		printf("%d\n",sum);
}

  

时间: 2024-10-12 03:01:07

POJ 1182 食物链 【并查集】的相关文章

POJ 1182 食物链 [并查集 带权并查集 开拓思路]

传送门 P - 食物链 Time Limit:1000MS     Memory Limit:10000KB     64bit IO Format:%I64d & %I64u Submit Status Practice POJ 1182 Appoint description:  System Crawler  (2015-01-27) Description 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物

poj 1182 食物链 并查集好题

挑战程序设计上有解答的并查集好题.把事件作为元素进行合并,举例:若输入1 2 3,意思就是把2,3归为同一类,至于归于哪一类并不需要去讨论,则把2属于A,3属于A这两件事件归为一类;2属于B,3属于B这两件事归为一类;2属于C,3属于C这两件事归为一类:若输入 2 2 3,由于A吃B,B吃C,C吃A,就把2属于A,3属于B这两件事情归为一类:以此类推.当检测到当前情况与之前正确的情况不符合,则错误的情况数加1. #include <iostream> #include <cstdio&g

poj 1182 食物链 并查集

Description 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A.  现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种.  有人用两种说法对这N个动物所构成的食物链关系进行描述:  第一种说法是"1 X Y",表示X和Y是同类.  第二种说法是"2 X Y",表示X吃Y.  此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的.当一句话满足

poj 1182 (关系并查集) 食物链

题目传送门:http://poj.org/problem?id=1182 这是一道关系型并查集的题,对于每个动物来说,只有三种情况:同类,吃与被吃: 所以可以用0,1,2三个数字代表三种情况,在使用并查集的时候再多加一个关系数组,初始时全部赋值为0 然后就是在进行并查集的每一步时加入关系的改变, 如果祖先节点相同,说明两者之间的关系已经出现,是已知的,判断两者关系之和与给的d-1是否相同 祖先节点不同,说明在此之前两者的关系还未知,就赋予关系 噢 对了 poj 上的这题要单组输入,多组会wa c

Poj(1182),种类并查集

题目链接:http://poj.org/problem?id=1182 再次熟练种类并查集,又积累点经验,和技巧,rank 0 2 1 先计算father[x] ,再更新rank[x]; #include <stdio.h> int father[50010]; int rank[50010]; int Find_Set (int x) { int tmp; if(x!=father[x]) { tmp = father[x]; father[x] = Find_Set(father[x]);

食物链 POJ 1182(并查集)

Description 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种. 有人用两种说法对这N个动物所构成的食物链关系进行描述: 第一种说法是"1 X Y",表示X和Y是同类. 第二种说法是"2 X Y",表示X吃Y. 此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的.当一句话满足下列三条之

poj 1182 (扩展并查集)

食物链 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 58979   Accepted: 17247 Description 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种. 有人用两种说法对这N个动物所构成的食物链关系进行描述: 第一种说法是"1 X Y",表示X和Y是同

又见关系并查集 以POJ 1182 食物链为例

简单的关系并查集一般很容易根据给出的关系搞出一个有向的环,那么两者之间的关系就变成了两者之间的距离. 对于此题: 若u,v不在一个集合内,则显然此条语句会合法(暂且忽略后两条,下同). 那么将fu 变为 fv的儿子时需加一条权值为 w 的边,w 满足(w + ru)%3 = (rv+ (D == 1? 0 : 1))%3(ru,rv分别为u,v与fv的关系,即距离). 之所以在D == 2时加 1,是因为u吃v表明着u到fv的距离比v到fv的距离大1. 同理,D == 1时,表明两者到fv的距离

poj 1182 食物链 (带关系的并查集)

  食物链 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 44835 Accepted: 13069 Description 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种. 有人用两种说法对这N个动物所构成的食物链关系进行描述: 第一种说法是"1 X Y",表示X和Y是同类.

POJ 1182 食物链【并查集】

题目链接:http://poj.org/problem?id=1182 此题利用并查集解决. 对于每只动物i创建3个元素i-A,i-B,i-C,并用这3*N个元素建立并查集. 1·i-x表示"i属于种类x" 2·并查集你的每一组表示组内所有元素代表的情况同时发生或不发生. 对于每一条信息,只需要按照下列操作即可: 1.第一种:x,y同类,合并x-A和y-A.x-B和y-B.x-C和y-C. 2.第二种:x吃y,,,合并x-A和y-B.x-B和y-C.x-C和y-A. 当然,在合并之前,