POJ1182 食物链(并查集)

题意:

中文题不解释

要点:

很经典的并查集问题,也很难。网上一共两种做法:一种是算相对类别偏移量,用了一系列找规律,难的爆炸,反正我看懂了但是再做一次肯定是做不出来的;另一种是将数组开三倍,表示所有的情况,这种我还可以尝试一下。


开三倍数组存储所有情况:

要点是将P[X]开成最大值的三倍,1~n表示当前x属于A;n+1~2n表示当前x属于B,2n+1~3n表示当前x属于C,每次合并时将x属于ABC的三种情况全部合并起来,这样就可以完全考虑。最后求假话数量,只要考虑对应的x属于的集合即可。这种方法内存虽然要求高了点,但实际运行时间还是少的。而且稍微好理解一点,还是用这种方法吧。

15316600 Seasonal 1182 Accepted 1276K 250MS C++ 1094B 2016-03-26 15:50:34
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define maxn 50050
int p[3*maxn],rank[3*maxn];//将x对应A,x对应B,x对应C都储存下来
int m, k;

void init()
{
	for (int i = 1; i <= 3 * m; ++i)
	{
		p[i] = i;
		rank[i] = 0;
	}
}
int find(int x)
{
	if (p[x] == x) return x;
	return p[x] = find(p[x]);
}
void merge(int x, int y)
{
	x = find(x);
	y = find(y);
	if (x == y) return;
	if (rank[x] > rank[y])
		p[y] = x;
	else
	{
		p[x] = y;
		if (rank[x] == rank[y]) rank[y]++;
	}
}
bool same(int x, int y)
{
	return find(x) == find(y);
}
int main()
{
	int d, x, y,num=0;
	scanf("%d%d", &m, &k);
	init();
	while (k--)
	{
		scanf("%d%d%d", &d, &x, &y);
		if (x > m || y > m || (d == 2 && x == y))
		{
			++num;
			continue;
		}
		if (d == 1)
		{
			if (same(x, y + m) || same(x, y + 2 * m)) ++num;//当x为A时y为B或C不满足条件
			else	//不用考虑x为B或C的问题,因为下面已经将x=ABC三种情况全合并起来了
			{		//如果出现x=A时y=B或C就说明x=B或C时也不满足条件
				merge(x, y);		//为A时x与y相等
				merge(x + m, y + m);
				merge(x + 2 * m, y + 2 * m);
			}
		}
		if (d == 2)
		{
			if (same(x, y) || same(x, y + 2 * m)) ++num;//当x, y同类,或者属于A的x吃了C的y,则为假话
			else
			{
				merge(x, y + m);	//属于A的x吃了属于B的y
				merge(x + m, y + 2 * m);//属于B的x吃了属于C的y
				merge(x + 2 * m, y);	//属于C的x吃了属于A的y
			}
		}
	}
	printf("%d\n", num);
	return 0;
}


相对类别偏移量:

参考博客:点击打开链接

15316265 Seasonal 1182 Accepted 508K 282MS C++ 833B 2016-03-26 14:56:41
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define maxn 50050
int n, k;
int p[maxn], rank[maxn];//rank[x]用来表示x与p[x]的食物链关系

void init()
{
	for (int i = 1; i <= n; ++i)
	{
		p[i] = i;
		rank[i] = 0;
	}
}
int find(int x)
{
	if (p[x] != x)
	{
		int t = p[x];
		p[x] = find(p[x]);
		rank[x] = (rank[x] + rank[t]) % 3;//找规律得到
	}
	return p[x];
}
void merge(int x, int y,int d)
{
	int xf = find(x);
	int yf = find(y);
	p[xf] = yf;
	rank[xf] = (rank[y] - rank[x] + 2 + d) % 3;
}

int main()
{
	int d, x, y,num=0;
	int xf, yf;
	scanf("%d%d", &n, &k);
	init();
	while (k--)
	{
		scanf("%d%d%d", &d, &x, &y);
		if (x > n || y > n || (d == 2 && x == y))
			num++;
		else
		{
			xf = find(x);
			yf = find(y);
			if (xf == yf)//等于才能判断,不等于判断不了可以直接合并
			{
				if ((rank[x] - rank[y] + 3) % 3 != d - 1)
					num++;
			}
			else
				merge(x, y, d);
		}	
	}
	printf("%d\n", num);
	return 0;
}

posted @ 2016-03-26 16:12  seasonal  阅读(140)  评论(0编辑  收藏  举报