P4782 【模板】2-SAT 问题

\(\color{#0066ff}{ 题目描述 }\)

\(n\)个布尔变量\(x_1\)~\(x_n\),另有\(m\)个需要满足的条件,每个条件的形式都是“\(x_i\)\(true/false\)\(x_j\)\(true/false\)”。比如“\(x_1\)为真或\(x_3\)为假”、“\(x_7\)为假或\(x_2\)为假”。2-SAT 问题的目标是给每个变量赋值使得所有条件得到满足。

$\color{#0066ff}{ 输入格式 } $

第一行两个整数n和m,意义如体面所述。

接下来m行每行4个整数 i a j b,表示“\(x_i\)为a或\(x_j\)为b”(a,b∈{0,1})

\(\color{#0066ff}{输出格式}\)

如无解,输出“IMPOSSIBLE”(不带引号); 否则输出"POSSIBLE"(不带引号),下 一行n个整数\(x_1\)~\(x_n\)\(x_i\)∈{0,1}),表示构造出的解。

\(\color{#0066ff}{输入样例}\)

3 1
1 1 3 0

\(\color{#0066ff}{输出样例}\)

POSSIBLE
0 0 0

\(\color{#0066ff}{数据范围与提示}\)

有spj

\(n ,m\leq 10^6\)

\(\color{#0066ff}{ 题解 }\)

2——SAT板子题

建图

每个变量x拆成x和x+n

x代表x取true的状态,x+n代表x取false的状态

图中有向边的含义:\(x->y\)有状态x, 必有状态y

比如\(x|y=1\)也就是说x和y至少有一个是1

就这样连\(x+n->y, y+n->x\)

x为0时,要保证式子成立,y必须为1

这样就能建好图了

考虑一个环,一个知道其中一个点的状态,其它的都出来了,所以锁点一下

当x和x+n在一个强联通分量里就不行了,冲突了,无解

现在考虑怎么输出方案

当 \(x\) 所在的强连通分量的拓扑序在 \(x+n\) 所在的强连通分量的拓扑序之后取 \(x\) 为真即可

在tarjan的过程中,实际上我们强联通分量的编号就是倒着的拓扑序qwq

Code

#include<bits/stdc++.h>
#define LL long long
LL in() {
	char ch; LL x = 0, f = 1;
	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
	return x * f;
}
const int maxn = 2e6 + 100;
struct node {
	int to;
	node *nxt;
	node(int to = 0, node *nxt = NULL): to(to), nxt(nxt) {}
	void *operator new(size_t) {
		static node *S = NULL, *T = NULL;
		return (S == T) && (T = (S = new node[1024]) + 1024), S++;
	}
};
node *head[maxn];
bool ins[maxn];
int st[maxn], top, bel[maxn], cnt, n, m, tot;
int dfn[maxn], low[maxn];
void add(int from, int to) {
	head[from] = new node(to, head[from]);
}
void tarjan(int x) {
	dfn[x] = low[x] = ++tot;
	ins[st[++top] = x] = true;
	for(node *i = head[x]; i; i = i->nxt) {
		if(!dfn[i->to]) {
			tarjan(i->to);
			low[x] = std::min(low[x], low[i->to]);
		}
		else if(ins[i->to]) low[x] = std::min(low[x], dfn[i->to]);
	}
	if(dfn[x] == low[x]) {
		cnt++;
		do {
			bel[st[top]] = cnt;
			ins[st[top]] = false;
			top--;
		}while(st[top + 1] != x);
	}
}
bool judge() {
	for(int i = 1; i <= n; i++) printf("i = %d, bel = [%d,%d]\n", i, bel[i], bel[i + n]);
	for(int i = 1; i <= n; i++) if(bel[i] == bel[i + n]) return true;
	return false;
}
int main() {
	n = in(), m = in();
	int x, y, xx, yy;
	for(int i = 1; i <= m; i++) {
		x = in(), xx = in(), y = in(), yy = in();
		if(xx && yy) add(x + n, y), add(y + n, x);
		if(xx && !yy) add(x + n, y + n), add(y, x);
		if(!xx && yy) add(x, y), add(y + n, x + n);
		if(!xx && !yy) add(x, y + n), add(y, x + n);
	}
	for(int i = 1; i <= n << 1; i++) if(!dfn[i]) tarjan(i);
	if(judge())	printf("IMPOSSIBLE\n");
	else {
		printf("POSSIBLE\n");
		for(int i = 1; i <= n; i++) 
			printf("%d%c", bel[i] < bel[i + n]? 1 : 0, i == n? '\n' : ' ');
	}
	return 0;
}
posted @ 2019-01-29 19:01  olinr  阅读(305)  评论(1编辑  收藏  举报