POJ 1182 食物链

1.题目描述:点击打开链接

2.解题思路:本题利用并查集解决。根据题意知,需要三个并查集,这里为了方便,给数组par均分为三段,分别记为A,B,C。通过下标来控制不同的集合。

那么接下来该如何处理每一条信息呢?当输入第一种信息时,表示u和v属于同一类,此时有三种可能,因此要把u和v分别合并到A,B,C中。如果是第二种,表示u吃v,那么也有三种情况:u在A中v在B中,u在B中v在C中,u在C中v在A中。

那么还有一个问题,如何判断信息是否合法呢?由于我们是把一个数组切割成了三大段,那么A,B,C其实是在同一组中的,而同一组中的所所有元素代表的情况要么同时发生,要么都不发生。比如如果u属于A和v属于B在同一个组里,那么如果u属于A为真,那么必有v属于B为真,反之同理。所以,只用判断u,v是否在同一个组中即可知该句话为真还是为假。

本题需要对并查集的性质深刻理解才能正确解题。

3.代码:

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;

#define N 150000+10
int par[N], rnk[N];
int n, m;
void init(int n)
{
	for (int i = 0; i < n; i++)
	{
		par[i] = i;
		rnk[i] = 0;
	}
}
int find(int x)
{
	if (par[x] == x)return x;
	else return par[x] = find(par[x]);
}
bool same(int x, int y)
{
	return find(x) == find(y);
}
void unit(int x, int y)
{
	x = find(x); y = find(y);
	if (x == y)return;
	if (rnk[x] < rnk[y])par[x] = y;
	else
	{
		par[y] = x;
		if (rnk[x] == rnk[y])rnk[x]++;
	}
}
int main()
{
	//freopen("t.txt", "r", stdin);
	scanf("%d%d", &n, &m);
	init(3 * n);
	int cnt = 0;
	for (int i = 1; i <= m; i++)
	{
		int id, u, v;
		scanf("%d%d%d", &id, &u, &v);
		u--, v--;
		if (u >= n || v >= n || v < 0 || u < 0)cnt++;//编号非法
		else if (id == 1)
		{
			if (same(u, v + n) || same(u, v + 2 * n))cnt++;//u和v实际上不属于同一个种类
			else
			{
				unit(u, v); unit(u + n, v + n); unit(u + 2 * n, v + 2 * n);//3种可能的情况
			}
		}
		else
		{
			if (same(u, v) || same(u, v + 2 * n))cnt++;//u和v是同一类 或者 实际上是v吃u(三种情况中只用挑一种即可)
			else
			{
				unit(u, v + n); unit(u + n, v + 2 * n); unit(u + 2 * n, v);
			}
		}
	}
	printf("%d\n", cnt);
	return 0;
}

你可能感兴趣的:(并查集)