【学习笔记】2-SAT

2-SAT 用来解决一类如下形式问题:有 \(n\) 个针对布尔变量的或条件,求一组解。

考虑按照命题连边,也就是从中找到必要的逻辑关系进行建图。那么显然,有两条边:一条是原命题,一条是逆否命题。

建图之后跑缩点,那么缩在一起的点一定是可以互相推出的。

那么,如果 \(x\)\(!x\) 合并到了一起,那么它就是无解的。否则,考虑按照拓扑序逆序构造解,看 \(x\) 对应的变量哪个在后,选择在后的那个为 \(1\) 即可。

考虑证明在 2-SAT 问题对称性下算法的正确性。

考虑按照拓扑序从大到小选择点,那么每次我选择这个点,然后把它对应的非变量染色为不选。那么每次我染为不选的都一定是一条前缀链。

那么这样,在保证 有解 的情况下,一定不会有两个互逆变量同时被选择,现在只需要证明不会有两个变量被同时不选即可。

有如下情况:

  • 假设 \(y\) 的拓扑序大,它被选择,并且连了两条不同的边指向 \(x,!x.\)

那么把图建立出来会发现,\(!y\) 的拓扑序是比 \(y\) 大的。条件不成立。

  • 假设 选择了 \(y,z\) 并且它们分别指向 \(x,!x.\)

建立出逻辑图之后,发现 \(!z,!y\) 的拓扑序大于 \(y,z,\) 题设不成立。

  • 假设选择了 \(y\) 并且它染色为不选的链上挂着 \(x,!x.\)

考虑建立逻辑图之后,发现会形成环。上述链当形如:\((y)\to (!y)\to (a)\to (x)\to (!a)\to (!x)\)

这里 \((!a)\to (y)\) 是存在的边,所以无解,与题设不成立。

综上,可以得出 2-SAT 算法的正确性。

#include <bits/stdc++.h>
using namespace std;
typedef double db;
//#define int long long
#define fi first
#define se second
#define mk make_pair
#define pb emplace_back
#define poly vector<int>
#define Bt(a) bitset<a>
#define bc __builtin_popcount
#define pc putchar
#define ci const int&
const int mod = 1e9 + 7;
const db eps = 1e-10;
inline int Max(ci x, ci y) {return x > y ? x : y;}
inline int Min(ci x, ci y) {return x < y ? x : y;}
inline db Max(db x, db y) {return x - y > eps ? x : y;}
inline db Min(db x, db y) {return x - y < eps ? x : y;}
inline int Add(ci x, ci y, ci M = mod) {return (x + y) % M;}
inline int Mul(ci x, ci y, ci M = mod) {return 1ll * x * y % M;}
inline int Dec(ci x, ci y, ci M = mod) {return (x - y + M) % M;}
typedef pair<int, int> pii;
inline int Abs(int x) {return x < 0 ? -x : x;}
//char buf[1<<21],*p1=buf,*p2=buf;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char Obuf[105000],*O=Obuf;//Siz shoule be the size of Out File
int pst[30],ptop;
inline void Fprint(){fwrite(Obuf,1,O-Obuf,stdout);}
inline void Fwrite(int x){
  if(x==0){*O++='0';return;}
  if(x<0)*O++='-',x=-x;ptop=0;
  while(x)pst[++ptop]=x%10,x/=10;
  while(ptop)*O++=pst[ptop--]+'0';
  if(O-Obuf>100000)Fprint(),O=Obuf;
}
inline int read() {
    int s = 0, w = 1;
    char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') w = -1;ch = getchar();}
    while (isdigit(ch)) {s = s * 10 + ch - '0';ch = getchar();}
    return s * w;
}
inline void write(int x) {
    if (x < 0)putchar('-'), x = -x;
    if (x > 9)write(x / 10);
	pc(x % 10 + '0');
}
inline int qpow(int x, int y) {
    int res = 1;
    while (y) {if (y & 1)res = Mul(res, x);x = Mul(x, x);y >>= 1;}
    return res;
}
inline void cadd(int &x, int y) {x += y;}
inline void cmul(int &x, int y) {x *= y;}
inline void cmax(int &x, int y) {x = Max(x, y);}
inline void cmin(int &x, int y) {x = Min(x, y);}
const int N = 2e6 + 10;
namespace Refined_heart{
	poly G[N];
	inline void link(int x,int y){G[x].pb(y);}
	int dfstime,top,st[N],dfn[N],low[N],ccf;
	int n,m,col[N],inst[N];
	void tarjan(int x){
		dfn[x]=low[x]=++dfstime;
		st[++top]=x;inst[x]=1;
		for(auto v:G[x]){
			if(!dfn[v]){
				tarjan(v);
				cmin(low[x],low[v]);
			}
			else if(inst[v])cmin(low[x],dfn[v]);
		}
		if(low[x]==dfn[x]){
			int y=0;++ccf;
			while(y=st[top--]){
				col[y]=ccf;
				inst[y]=0;
				if(x==y)break;
			}
		}
	}
	void solve(){
		n=read();m=read();
		for(int i=1;i<=m;++i){
			int a=read(),va=read(),b=read(),vb=read();
			if(va&&vb)link(a+n,b),link(b+n,a);
			if(!va&&!vb)link(a,b+n),link(b,a+n);
			if(!va&&vb)link(a,b),link(b+n,a+n);
			if(va&&!vb)link(a+n,b+n),link(b,a);
		}
		for(int i=1;i<=n+n;++i)if(!dfn[i])tarjan(i);
		for(int i=1;i<=n;++i){
			if(col[i]==col[i+n]){
				puts("IMPOSSIBLE");
				exit(0);
			}
		}
		puts("POSSIBLE");
		for(int i=1;i<=n;++i){
			if(col[i]<col[i+n])pc('1');
			else pc('0');
			pc(' ');
		}
	}
}
int main(){
	Refined_heart::solve();
	return 0;
}


posted @ 2021-11-18 21:45  Refined_heart  阅读(31)  评论(0编辑  收藏  举报