【學習筆記】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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章