2-SAT 问题及图论解法

定义 给定 n 个 bool 型变量和 m 个关系式,形如 (x_i) 或(非) (x_j)。求问是否矛盾,不矛盾则需要求出一组解。

解法 建立一个 (2n) 个节点的有向图。第 (2i-1) 与第 (2i) 个节点分别表示 (x_i) 为真与 (x_i) 为假。如果是 (x_i)(x_j) 在有向图中从 (x_i) 假连向 (x_j) 真,并从 (x_j) 假连向 (x_i) 真,表示二者中至少有一个为真,其他情况类似。然后求整个图的强连通分量。如果 (x_i) 假与 (x_i) 真在同一强连通分量中则矛盾。如果不矛盾,则如果 (x_i) 为真所在的强连通分量的拓扑序在 (x_i) 为假的强连通分量前则 (x_i) 为真即可,正确性显然,而拓扑序其实就是 tarjan 算法里面标好的每个强连通分量的前后值,不过是反的罢了。复杂度 (O(n+m))

洛谷板子题

#include<cstdio>
#include<vector>

const int maxn = 1E+6 + 5;

int n, m, ind, cnt;
int dfn[maxn], low[maxn];
int top, sta[maxn], scc[maxn];
bool insta[maxn];
std::vector<int> to[maxn];

void DFS(int u) {
	dfn[u] = low[u] = ++ind;
	sta[++top] = u, insta[u] = 1;
	
	for(auto v : to[u]) {
		if(!dfn[v]) DFS(v), low[u] = std::min(low[u], low[v]);
		else if(insta[v]) low[u] = std::min(low[u], dfn[v]);
	}
	
	if(low[u] == dfn[u]) {
		++cnt;
		while(top >= 1 && sta[top] != u) {
			insta[sta[top]] = 0;
			scc[sta[top--]] = cnt;
		}
		insta[sta[top]] = 0;
		scc[sta[top--]] = cnt;
	}
}

int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1, x, y, a, b; i <= m; ++i) {
		scanf("%d%d%d%d", &x, &a, &y, &b);
		
		to[2 * x - (a ^ 1)].push_back(y * 2 - b);
		to[2 * y - (b ^ 1)].push_back(x * 2 - a);
	}
	
	for(int i = 1; i <= n * 2; ++i)
		if(!dfn[i]) DFS(i);
	
	for(int i = 1; i <= n; ++i)
		if(scc[i * 2 - 1] == scc[i * 2])
			return puts("IMPOSSIBLE"), 0;
	
	puts("POSSIBLE");
	for(int i = 1; i <= n; ++i)
		printf("%d ", scc[i * 2] < scc[i * 2 - 1]);
}
原文地址:https://www.cnblogs.com/whx1003/p/13543820.html