【ybt金牌导航3-6-2】【luogu UVA11294】【POJ 3648】婚礼 / Wedding

婚礼 / Wedding

题目链接:ybt金牌导航3-6-2 / luogu UVA11294 / POJ 3648

题目大意

有一堆夫妇,然后有两边,同一对夫妇不能在同一边。
同时再给出一些条件,就是不能让某两个人同时在左边。

问你是否有成立的情况,然后如果有,输出其中一种合法的方案。

思路

首先,你看到题目,自然会想到用 2-set 来做。

然后你考虑建边,对于每一对,有男的在新郎旁边和女的在新郎旁边。
然后如果有冲突,就互相连到互相的另一半。

然后这里有个关键的点就是新娘要连一条边到新郎。
为什么呢?
那你想,只有新郎是有限制的,新娘这边就是随便坐。
那我们 2-set 肯定要让电脑找的是新郎的这边啊。
那怎么弄呢?我们就可以这样连边,这样如果选了新娘就一定要选新郎,就会出现错误。但如果徐娜了新郎就不会有锅。

然后我们来看看怎么找答案 。
2-set 找答案是利用拓扑序,然后还是逆拓扑序,因为按逆的就不会产生冲突,不用逆的有可能会跟前面已经排好的产生冲突。
然后你会发现逆拓扑序其实就是你缩点的顺序,那就不用再跑一遍求了。

然后怎么通过逆拓扑序看放那边呢?
因为你 2-set 选的是新郎的,答案是要新娘的,我们只要到时反过来即可。
然后现在说的是新郎那边的。
那如果对于某一对,那它两个点会各有逆拓扑序。那你肯定是先处理逆拓扑序前的,也就是拓扑序后的,那就选这个点所对应的。
(记得在这道题要反过来!!!)

然后就可以了。

代码

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>

using namespace std;

struct node {
	int to, nxt;
}e[500010];
int n, m, x, y, le[5001], KK;
char cx, cy, answer[5001];
int dfn[5001], low[5001], tmp;
int sta[5001], in[5001], n_n;
bool cant;

void csh() {//初始化
	memset(e, 0, sizeof(e));
	memset(le, 0, sizeof(le));
	KK = 0;
	memset(dfn, 0, sizeof(dfn));
	memset(low, 0, sizeof(low));
	tmp = 0;
	memset(sta, 0, sizeof(sta));
	memset(in, 0, sizeof(in));
	n_n = 0;
	cant = 0;
}

void add(int x, int y) {
	e[++KK] = (node){y, le[x]}; le[x] = KK;
}

int another(int x) {
	if (x > n) return x - n;
	return x + n;
}

void tarjan(int now) {//Tarjan缩点
	dfn[now] = low[now] = ++tmp;
	sta[++sta[0]] = now;
	
	for (int i = le[now]; i; i = e[i].nxt)
		if (!dfn[e[i].to]) {
			tarjan(e[i].to);
			low[now] = min(low[now], low[e[i].to]);
		}
		else if (!in[e[i].to]) low[now] = min(low[now], low[e[i].to]);
	
	if (dfn[now] == low[now]) {
		in[now] = ++n_n;
		while (sta[sta[0]] != now) {
			in[sta[sta[0]]] = n_n;
			sta[0]--;
		}
		sta[0]--;
	}
	
	return ;
}

int main() {
	scanf("%d %d", &n, &m);
	while (n || m) {
		csh();
		
		for (int i = 1; i <= m; i++) {
			scanf("%d%c %d%c", &x, &cx, &y, &cy);
			x++;
			y++;
			if (cx == 'h') x += n;
			if (cy == 'h') y += n; 
			add(x, another(y));//建图
			add(y, another(x));
		}
		add(1, 1 + n);//让程序选新郎那边的,因为新娘那边没有限制
		
		for (int i = 1; i <= 2 * n; i++)
			if (!dfn[i]) tarjan(i);
		
		for (int i = 1; i <= n; i++)//2-sat 的判断矛盾
			if (in[i] == in[n + i]) {
				cant = 1;
				printf("bad luck\n");
				break;
			}
		if (cant) {
			scanf("%d %d", &n, &m);
			continue;
		}
		
		for (int i = 2; i <= n; i++) {
			printf("%d", i - 1);//根据拓扑排序
			if (in[i] > in[i + n]) printf("w");
				else printf("h");
			printf(" ");
		}
		printf("\n");
		
		scanf("%d %d", &n, &m);
	}
	
	return 0;
}
上一篇:奖励关题解


下一篇:超声波测距+蓝牙与App通信+控制开发板灯亮灭