POJ 1182 - 食物链

题目大意:中文题。

解题思路:这题只有一个样例,如果按文件结束跳出循环的话就WA。做法是这样的将每只动物定义为一个结构体,结构体包括了此动物的上级,以及与上级的关系(0:同类,1:被吃,2:吃上级)。初始时,所有动物的上级是自身,与上级都是同类。前两个条件很容易判断不说了,最后一个条件与前面的关系有矛盾才是重点。

有矛盾的情况分两种判断,一:两动物归为同类时有矛盾;如果两只动物的最上级是一致的且与最上级的关系是不一致的,此时发生矛盾,否则说明没有矛盾,我们可以确立一个关系。二:两动物归为前者吃后者时的矛盾;如果两只动物的最上级是一致的且根据两者最上级的关系判断出,并非前者吃后者时矛盾。而能够得到这个判断的原因就在于我们建立关系的方式、一开始我们并不知道那些动物属于同类动物,在输入的过程中,关系建立起来,有矛盾也是先有了一些关系的基础才能确定。关系建立自然包括吃与被吃、同类。两者之间有关系就说明之前建立过关系,使得两动物的最上级一致。

主要是关于关系的改变的地方比较迷,包括查找中的优化和合并中的关系变更。查找时寻找上级是与一般的并查集一致的,关系改变就是题解提到的向量转移方式,很强。我理解大概如下:

将与上级的关系当作一个叫偏移量的,指向关系要明确,是上级指向下级。偏移量为0时表示同类,偏移量为1时表示上级要吃你,偏移量为2时表示上级被你吃,如果大于2则就模3。两动物之间的关系就是根据偏移量来的,比如(查找操作的原理。。。我认为的)A动物上级为B,偏移量为x(B->A),B动物上级为C,偏移量为y(C->B)。这时A与C的关系就是一条线的关系,指向关系要明确(C->B  B->A),所以A动物上级变更为C,与上级关系变更为y + x。又比如(合并操作的原理。。。我认为的)A动物上级为B,偏移量为x,C动物上级为D,偏移量为y,A动物与C之间一个关系z(关系建立前将A当作C的上级,偏移量就是输入的1或2减1)。那么A、C合并怎么做的。如下:将A上级与C上级建立关系,一条线(B->A  A->C  C->D或D->C  C->A A->B)代码用的是后者。所以B->D就等于x+z-y,为了确保结果是正数加个3。

提到的上级都是优化以后的最高上级,为什么用上级来称,看之前的题解。具体看题解。。。http://blog.csdn.net/niushuai666/article/details/6981689

ac代码:

#include <iostream>
using namespace std;
int n, m, t1, t2, sum, temp, a, b;
struct node{
	int parent;
	int relation;
}an[50005];

int find(int x)
{
	int r;
	if (an[x].parent == x)
		return x;
	r = an[x].parent;
	an[x].parent = find(r);
	an[x].relation = (an[r].relation + an[x].relation) % 3;
return an[x].parent;
}

int main()
{
	scanf("%d%d", &n, &m);
	sum = 0;
	for (int i=1; i<=n; i++){
		an[i].parent = i;
		an[i].relation = 0;
	}
	for (int i=0; i<m; i++){
		scanf("%d%d%d", &temp, &t1, &t2);
		a = find(t1), b = find(t2);
		if (t1 > n || t2 > n || (temp == 2 && t1 == t2))
			sum++;
		else if (temp == 1){
			if (a == b)
				sum += (an[t1].relation != an[t2].relation);
			else {
				an[b].parent = a;
				an[b].relation = (3 + temp - 1 + an[t1].relation - an[t2].relation) % 3;
			}
		}
		else{
			if (a == b)
				sum += ((3 - an[t1].relation + an[t2].relation) % 3 != temp - 1);
			else {
				an[b].parent = a;
				an[b].relation = (3 + temp - 1 + an[t1].relation - an[t2].relation) % 3;	
			}
			
		}
	}
	printf("%d\n", sum);
return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值