HDU-5971 ___Wrestling Match——二分图染色 或 种类并查集

题目链接:传送门

题目大意:

    给出 n n 个人, m m 场 比赛 x x 个已经确定的好人 y y 个已经确定的坏人。
    每场比赛都由 好人和坏人 组成。
    问是否能够将每个人划分成好人或者坏人。

解题思路:

    这里介绍用并查集的思想
    用一个数组记录着 m m 对人的相互关系
    后面确认好(坏)人的时候,若这个人已经确定了是好人,则判断这个人的状态和数组中对应的状态是否相同

    最后、出现矛盾有以下种情况:
合并时发现祖先相同却属于同一种人
确认好(坏)人时这个人的属性相反
确定这个人是好人时,这个人在数组中的状态与之前的矛盾
相反
n个人没有全部访问

代码思路:

    因为这里保存的是相互关系,所以合并的时候应该相互异或并异或1
    找祖先的时候很定也是相互异或
    在给出某个人的属性的时候,若他的祖先没有属性,则要用这个人的属性与祖先的状态(数组中的值)共同影响得出祖先的属性,即作异或处理

核心:并查集的花式使用,同时使用二分图染色应该简单一点

#include<bits/stdc++.h>
using namespace std;

const int N = 2005;
int f[N], num[N], vis[N], kind[N];
int n, m, u, v, x, y, temp, conf;

int find(int x) {
	if(x != f[x]) {
		int fa = f[x];
		f[x] = find(f[x]);
		num[x] = num[x] ^ num[fa];
	}
	return f[x];
}

void init(int n) {
	conf = 1;
	for(int i=0 ; i<=n; i++) {
		f[i] = i;
		num[i] = vis[i] = 0;
		kind[i] = -1;
	}
}

int main() {
	while(~scanf("%d%d%d%d", &n, &m, &x, &y)) {
		init(n);
		for(int i=0; i<m; i++) {
			scanf("%d%d", &u, &v);
			int fa = find(u), fb = find(v);
			if(fa == fb) {
				if(num[u] == num[v])    conf = 0;
			} else {
				f[fb] = fa;
				num[fb] = 1 ^ num[v] ^ num[u];
			}
			vis[u] = vis[v] = 1;
		}

		for(int i=0; i<x; i++) {
			scanf("%d", &temp);
			vis[temp] = 1;
			if(kind[temp]==1) conf = 0;
			kind[temp] = 0;
			int fx = find(temp);
			if(kind[fx]>=0) 
				conf = conf && (kind[temp]^num[temp]==kind[fx]);
			else kind[fx] = kind[temp]^num[temp];
		}

		for(int i=0; i<y; i++) {
			scanf("%d", &temp);
			vis[temp] = 1;
			if(kind[temp]==0) conf = 0;
			kind[temp] = 1;
			int fx = find(temp);
			if(kind[fx]>=0) 
				conf = conf && (kind[temp]^num[temp]==kind[fx]);
			else kind[fx] = kind[temp]^num[temp];
		}

		for(int i=1; i<=n; i++)
			conf = conf && vis[i];

		if(conf) puts("YES");
		else puts("NO");
	}
}

猜你喜欢

转载自blog.csdn.net/Scar_Halo/article/details/83388221
今日推荐