【洛谷P4782】【模板】2SAT 问题

题目

题目链接:https://www.luogu.com.cn/problem/P4782
\(n\) 个布尔变量 \(x_1\)\(\sim\)\(x_n\),另有 \(m\) 个需要满足的条件,每个条件的形式都是 「\(x_i\)true / false\(x_j\)true / false」。比如 「\(x_1\) 为真或 \(x_3\) 为假」、「\(x_7\) 为假或 \(x_2\) 为假」。
2-SAT 问题的目标是给每个变量赋值使得所有条件得到满足。

思路

对于一个要求 「\(x\)true\(y\)true」,它的意思就是 「如果 \(x\)false 那么 \(y\) 必须是 true,如果 \(y\)false 那么 \(x\) 必须是 false」。
把每个点拆成 truefalse 两个点。在上例中,我们就从 \(x_f\)\(y_t\) 连边,\(y_f\)\(x_t\) 连边。
那么如果若干个点在一个强连通分量内,那么他们的取值是一样的。
所以 tarjan 缩点,记录每一个点属于哪一个强连通分量 \(col[x]\),显然如果 \(col[x_t]=col[x_f]\),那么无解。

代码

#include <stack>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=2000010;
int n,m,cnt,tot,head[N],dfn[N],low[N],col[N];
bool vis[N];
stack<int> st;

struct edge
{
	int next,to;
}e[N*2];

void add(int from,int to)
{
	e[++tot].to=to;
	e[tot].next=head[from];
	head[from]=tot;
}

void tarjan(int x)
{
	dfn[x]=low[x]=++tot;
	st.push(x); vis[x]=1;
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (!dfn[v])
		{
			tarjan(v);
			low[x]=min(low[x],low[v]);
		}
		else if (vis[v])
			low[x]=min(low[x],dfn[v]);
	}
	if (dfn[x]==low[x])
	{
		cnt++; int y;
		do {
			y=st.top(); st.pop();
			col[y]=cnt; vis[y]=0;
		} while (x!=y);
	}
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for (int i=1,x,y,fx,fy;i<=m;i++)
	{
		scanf("%d%d%d%d",&x,&fx,&y,&fy);
		add(x+n*(fx^1),y+n*fy);
		add(y+n*(fy^1),x+n*fx); 
	}
	tot=0;
	for (int i=1;i<=n*2;i++)
		if (!dfn[i]) tarjan(i);
	for (int i=1;i<=n;i++)
		if (col[i]==col[i+n]) return printf("IMPOSSIBLE"),0;
	printf("POSSIBLE\n");
	for (int i=1;i<=n;i++)
		if (col[i]>col[i+n]) printf("1 ");
			else printf("0 ");
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/13154216.html