浅谈2-SAT,理解万岁!

2-SAT

定义什么的我也说不明白
是形如

\[(x_{i}\vee x_{j})\wedge (x_{k}\vee x_{l})\wedge ......\wedge (x_{n}\vee x_{m}) \]

这样的一个问题,其中 \(x\) 是布尔变量,可以看作一个条件,符合为真\((1)\),反之为假\((0)\)\(\vee\) 可以理解为逻辑或,\(\wedge\) 可以理解为逻辑与。

我们要做的,就是找到一组可行解使上面的式子为真,也就是满足所有条件。

我觉得这是一种图论的思想吧(

就是考虑这样的东西:

\[x_{1} = (a == 1) ? 1 : 0 \]

\[x_{2} = (b == 0) ? 1 : 0 \]

要令 \((\neg x_{1} \vee x_{2})\) 为真

这里的 $\neg $ 就是“非”。

那么无非就三种情况:

  • \(x_{1} = 0 , x_{2} = 1\) 此时 \(\neg x_{1} = 1,x_{2} = 1\)
  • \(x_{1} = 1 , x_{2} = 1\) 此时 \(\neg x_{1} = 0,x_{2} = 1\)
  • \(x_{1} = 0 , x_{2} = 0\) 此时 \(\neg x_{1} = 1,x_{2} = 0\)

我们发现第一种可以先忽略,因为他算不上什么约束条件。

看后面两种,发现如果 \(\neg x_{1}\) 或者 \(x_{2}\) 为假,都要求另一个为真,据此我们可以建图:

  • \(x_{1} -> x_{2}\)\(x_{1}\) 为真,那么 \(x_{2}\) 必须为真
  • \(\neg x_{2} -> x_{1}\)\(\neg x_{2}\) 为真,那么 \(x_{1}\) 必须为真

分别对应两者为假的情况。有点绕,但是理解一下就好了!

然后你发现一个强连通分量里的点他们的值应该是相等的。

所以直接缩点就好了,无解的情况就是 \(x\)\(\neg x\) 在同一个强连通分量里。

然后如何构造出可行解呢?

回想我们的连边方式,如果一个点向另一个点连边,那么被连的那个点一定是符合条件的那个!也就是我们需要的解。

这启示我们去弄一个拓扑序,对于 \(x\)\(\neg x\), 拓扑序大的就是我们要的!

其实在强连通分量 tarjan 的时候我们求出来的是一个反着的拓扑序,想象一颗 \(dfs\) 树,越靠近叶子结点的强连通分量在栈中会越先弹出来,也就是他的强连通编号越小。反正一句话,强连通编号越小,拓扑序越大。所以做法就显然了:对于一个 \(x\) ,比较他们的强连通编号,小的就是要的答案!

模板题

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i<(b);++i)
#define rrep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
template <typename T>
inline void read(T &x){
	x=0;char ch=getchar();bool f=0;
	while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(f)x=-x;
}
template <typename T,typename ...Args>
inline void read(T &tmp,Args &...tmps){read(tmp);read(tmps...);}
const int N = 2e6 + 5;
vector<int>g[N];
int n,m;
int dfn[N],low[N],sta[N],top,scccnt,dfncnt,sccnum[N];
void dfs(int u){
	low[u] = dfn[u] = ++dfncnt;
	sta[++top] = u;
	for(int v : g[u]){
		if(!dfn[v]){
			dfs(v);
			low[u] = min(low[u],low[v]);
		}
		else if(!sccnum[v])low[u] = min(low[u],dfn[v]);
	}
	if(low[u] == dfn[u]){
		++scccnt;
		while(1){
			int x = sta[top--];
			sccnum[x] = scccnt;
			if(x == u)break;
		}
	}
}
signed main(){
	read(n,m);
	rep(i,1,m){
		int x,a,y,b;
		read(x,a,y,b);
		g[x + n * (!a)].push_back(y + n * b);//x为假向y为真连边
		g[y + n * (!b)].push_back(x + n * a);//y为假向x为真连边
	}
	rep(i,1,n*2)if(!dfn[i])dfs(i);
	rep(i,1,n)if(sccnum[i] == sccnum[i+n])return puts("IMPOSSIBLE"),0;//无解
	puts("POSSIBLE");
	rep(i,1,n){
		printf("%d ",sccnum[i] > sccnum[i+n]);//取小的,我这里 (i+n) 代表x,i代表为非x
	}
}
posted @ 2022-09-03 11:41  Xu_brezza  阅读(47)  评论(1编辑  收藏  举报