2-SAT--UVA11294 Wedding

problem

给定\(n\)个布尔型变量\(x_i\)以及他们之间的逻辑关系例如\((x_j\lor x_i) \land (\lnot x_p\lor x_q)……\),求出一组合法解

solution

显然每个点都有两种状态:真和假。这样我们可以把\(n\)个点拆分成\(2n\)个点,\(x\)和\(\lnot x\).

对于每一对关系\((i\lor j)\),我们就可以\(\neg i\Longrightarrow j\)和\(\lnot j\Longrightarrow i\)这样连出两条边

那么现在变量\(x\)有四种连边的情况:

  • \(x\)和\(\lnot x\),此时之间没有边,随便取

  • \(x\Longrightarrow \lnot x\),此时\(x\)为假

  • \(\lnot x\Longrightarrow x\),此时\(x\)为真

  • \(x\Longrightarrow \lnot x\)且\(\lnot x\Longrightarrow x\),\(x\)又为真又为假,无解

那么如何求解的情况呢??看看上面的第四种情况就可以联想到\(tarjan\)s缩点,如果\(x\)和\(\lnot x\)在同一个强联通分量中就无解

如果\(x\)所在的强连通分量的拓扑序在\(\lnot x\)所在的强连通分量之后\(x\)为真(并不会证明)。

另外拓扑序其实就是\(tarjan\)染色的逆序,这样我们只需要判断\(x\)的颜色\(<\)\(\lnot x\)的颜色就ok了

例题

考虑对每个夫妇建两个点 \(x\),\(\lnot x\),\(x\)表示妻子与新郎同侧,\(\lnot x\)则表示丈夫与新郎同侧。

那么对于一对关系\((a,b)\),\(a\)向\(\lnot b\),\(b\)向\(\lnot a\)各连一条边,表示若\(a\)与新郎同侧,那么\(b\)不能与新郎同侧,也就是\(\lnot b\)一定要与新郎同侧,反之亦然。

输出时反过来,即若与新郎同侧的是妻子,那么与新娘同侧的(也就是需要输出的人)就是丈夫

(快读这东西就离谱)

code:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int read(){
	int x = 1,a = 0;char ch = getchar();
	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
	return x*a;
}

const int maxn = 4e5+10;

int n,m;

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

struct node{
	int to,nxt;
}ed[maxn*2];

int head[maxn*2],tot;

void add(int u,int to){
	ed[++tot].to = to;
	ed[tot].nxt = head[u];
	head[u] = tot;
}

int col[maxn*2],top,dfn[maxn*2],low[maxn*2],color,cnt,st[maxn*2];

void tarjan(int x){
	dfn[x] = low[x] = ++cnt;
	st[++top] = x;
	for (int i = head[x];i;i = ed[i].nxt){
		int to = ed[i].to;
		if (!dfn[to]){
			tarjan(to);
			low[x] = min(low[x],low[to]);
		}
		else if (!col[to]) low[x] = min(low[x],dfn[to]);
	}
	if (dfn[x] == low[x]){
		++color;
		int y;
		while (y = st[top--]){
			col[y] = color;
			if (x == y) break;
		}
	}
}

bool flag;

void init(){
	memset(head,0,sizeof(head));
	memset(ed,0,sizeof(ed));
	memset(col,0,sizeof(st));
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(st,0,sizeof(st));
	color = cnt = tot = top = 0;
	flag = true;
}

int main(){
	while (~scanf ("%d%d",&n,&m)&&(n+m) != 0){
		init();
		for (int i = 1;i <= m;i++){
			char op;
			int u ;scanf("%d",&u);scanf ("%c",&op);u++;
			if (op == 'h') u+=n;
			int v ;scanf("%d",&v);scanf ("%c",&op);v++;
			if (op == 'h') v+=n;
			add(u,getnum(v));
			add(v,getnum(u));
		}
		add(1,1+n);
		for (int i = 1;i <= 2*n;i++){
			if (!dfn[i]) tarjan(i);
		}
		for (int i = 1;i <= n&&flag;i++){
			if (col[i] == col[i+n]) flag = false;
		}
		if (!flag){printf("bad luck\n");continue;}
		for (int i = 2;i <= n;i++){
			if (col[i] > col[i+n]) printf("%dw ",i-1);
			else printf("%dh ",i-1);
		}
		puts("");
	}
	return 0;
}
上一篇:UVA1078 Steam Roller


下一篇:P2341 [USACO03FALL][HAOI2006]受欢迎的牛 G