2-SAT 入門

來源:互聯網
上載者:User

標籤:效果   else   isp   分享圖片   line   etc   head   ace   表示   

一、前置技能

 >  Tarjan求割點

 >  高中數學選修2-1 命題與邏輯關係(好像是這個名字)

 >  莓了

(本文用&&表示與 || 表示或  !x表示x的否命題 與正常的數學符號不同食用請注意)

 

二、k-SAT

 >  Q: 為什麼講2-SAT之前要先講k-SAT?

因為k-SAT被證明是NP-完全問題

所以暴搜就不去管它……

 >  Q: 所以什麼是“SAT”?

由美國大學委員會主辦的一場考試 

SAT被稱為布爾方程的可滿足性問題

就是判斷一組邏輯關係同時滿足的正確性……

比如a&&(!a) 就不滿足

再比如a&&b||c&&!d&&(.....)

我們給一種特殊的布爾方程起一個名字叫 合取範式 

就是形如(a || !b || c) && (d || e) && (...)

其中每個bool變數叫 文字 打了括弧的部分就叫 子句 

如果每個子句大小不超過k那麼就叫 k-SAT

同理 如果合取範式的每個子句中的文字個數都不超過兩個,那麼對應的 SAT 問題又稱為 2-SAT 問題。

 

三、解法

考慮一個二分圖 左邊表示bool變數值為0(偽) 右邊表示值為1(真)

如果滿足一個命題必然能推出另一個命題 那麼就在這兩個命題之間連一條邊

考慮兩個bool變數之間的關係無非是 && 和 || 

由於我們是2-SAT 所以最多出現a || b兩個變數

那麼轉化為蘊含(推出)關係

|| 運算的定義

!a => b 且 !b => a

然後是 &&

!a => a 且 !b => b  

 

 >  Q: 這……bool變數還能同時滿足真和偽?

當然不是 這裡的推出 可以理解為如果選擇了a為偽,那麼最後會推出a為真

所以我們最後選擇的時候不能選擇a為偽 只能選擇a為真 達到了我們的目的

 

 >  Q: 好吧……可是,為什麼要這麼建圖?

我們考慮bool運算的性質

命題成立則它的逆否命題成立 這個性質又叫 對稱性

同時考慮推出的 傳遞性  ,如果b是由某一個變數推知為真 和它為真的效果是一樣的 都能推知下一步的變數

所以拆完真偽最終在一個強連通分量裡的點的bool值相同(注意並不是變數的bool值相同)

同時上一個Q表示建圖可以處理單個變數定值的問題

 

 >  Q:嗯……如何求出最終答案呢?

我們對於最後建好的圖跑一個Tarjan縮點 如果變數的真值和偽值在同一個強連通分量裡(即既不能為真也不能為偽)那麼就無解

否則 每個點的答案應該是真值和偽值中拓撲序靠後的那個(同第一個Q)

(所以要縮點)

然後由於我們跑tarjan的時候 就是拓撲序靠後的強連通分量編號小

所以就不用建新圖辣

 

四、代碼

Code:(luogu P4782)

 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 #define itn int 6 #define ms(a,b) memset(a,b,sizeof a) 7 #define rep(i,a,n) for(int i = a;i <= n;i++) 8 #define per(i,n,a) for(int i = n;i >= a;i--) 9 using namespace std;10 typedef long long ll;11 ll read() {12     ll as = 0,fu = 1;13     char c = getchar();14     while(c < ‘0‘ || c > ‘9‘) {15         if(c == ‘-‘) fu = -1;16         c = getchar();17     }18     while(c >= ‘0‘ && c <= ‘9‘) {19         as = as * 10 + c - ‘0‘;20         c = getchar();21     }22     return as * fu;23 }24 const int N = 2000005;25 //head26 int n,m;27 int head[N],nxt[N<<1],mo[N<<1],cnt = -1;28 bool flag[N];29 void add(itn x,int y) {30     mo[++cnt] = y;31     nxt[cnt] = head[x];32     head[x] = cnt;33 }34 int rev(int x) {return x>n?x-n:x+n;}35 36 int dfn[N],low[N],col[N],stk[N];37 int scc,idx,top;38 bool ins[N];39 void tarjan(int x) {40     dfn[x] = low[x] = ++idx;41     stk[++top] = x,ins[x] = 1;42     for(int i = head[x];i;i = nxt[i]) {43     int sn = mo[i];44     if(!dfn[sn]) {45         tarjan(sn);46         low[x] = min(low[x],low[sn]);47     } else if(ins[sn]) low[x] = min(low[x],dfn[sn]);48     }49     if(dfn[x] == low[x]) {50     int t = -1;51     scc++;52     while(t!=x) {53         t = stk[top--];54         col[t] = scc;55         ins[t] = 0;56     }57     }58 }59 60 int main() {61     n = read();62     m = read();63     rep(i,1,m) {64     int x = read();x += read()*n;65     int y = read();y += read()*n;66     add(rev(x),y),add(rev(y),x);67     }68     rep(i,1,n<<1) if(!dfn[i]) tarjan(i);69     bool flag = 1;70     rep(i,1,n) if(col[i] == col[n+i]) flag = 0;71     if(!flag) puts("IMPOSSIBLE");72     else {73     puts("POSSIBLE");74     rep(i,1,n) printf("%d ",col[i] > col[i+n]);75     puts("");76     }77     return 0;78 }
View Code

注意:

1.跑的時候依然像Tarjan一樣考慮多個聯通塊

2.本題全部是或的關係 注意反向建圖

3.最後比較的時候每個人寫法不一樣要考慮大於還是小於

 

例題詳見Practice 1

By prophetB

2-SAT 入門

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.