2 sat學習筆記(補檔

2022-05-20 11:43:21 字數 3567 閱讀 6452

重學2-sat

給定乙個布林方程,判斷是否存在一組布林變數能滿足這個方程,方程的可行解被稱為\(sat\)。這個問題是\(np-hard\)的。但是如果我們對這個問題加上一些限制,就可以在多項式時間內求解了。設乙個布林方程為:

\((a_1\lor a_2\cdots\lor a_n) \land (b_1\lor b_2\cdots\lor b_n) \cdots\)

如果乙個布林方程滿足\(n<=2\),那麼這就是乙個\(2-sat\)問題。

因為每個布林變數只有兩種取值,所以我們可以將每個布林變數拆成兩個點後建圖。設將\(x\)拆成\(x\)和\(x'\)。\(x\)表示變數\(x\)值為真,\(x'\)表示變數\(x\)值為假。

如果從\(a\)的值,我們能推出\(b\)的值。那我們就在圖中連一條有向邊。比如乙個布林方程方程:\((a_1\lor a_2)\),我們發現如果\(a_1\)為假,那麼\(a_2\)一定為真;同理如果\(a_2\)為假,那麼\(a_1\)一定為真。於是我們在圖中建兩條有向邊:\(a_1'->a_2\)和\(a_2'->a_1\)。這就是最基本的\(2-sat\)建圖方法。

對於乙個變數\(x\),如果點\(x\)和點\(x'\)在同乙個強連通分量中,那麼顯然這個布林方程無解,因為此時\(x\)的值既要為真,又要為假。

如果乙個布林方程存在一組解,對於點\(x\),如果\(x\)的拓撲序在\(x'\)之後,那麼答案就是\(x\)取真。反之,\(x\)就取假。因為這樣能夠避免衝突,構造出一組可行解

先在圖中求一遍強連通分量。可以用\(kosaraj\)或者\(tarjan\)。注意\(kosaraju\)中強連通分量編號的順序就是該圖強連通分量的拓撲序。而在\(tarjan\)則是強聯通分量編號順序則和該圖強連通分量拓撲序相反。

**中強聯通分量用\(kosaraju\)求解

graph::kosaraju();

for (int i = 1; i <= n; ++i)

}puts("possible");

for (int i = 1; i <= n; ++i)

題意
求解\(2-sat\)問題

題解

對每種情況分別連邊即可。設當前兩個變數分別為\(a\)和\(b\)

\(\begin

a'->b, b'->a & a真b真\\

a'->b', b->a & a真b假 \\

a->b, b'->a' & a假b真 \\

a->b', b->a' & a假b假

\end\)

然後按照上面講的方法跑\(kosaraju\)求解即可。

// author: 23forever

#include #define pb push_back

#define pii pair#define mp make_pair

#define fi first

#define se second

typedef long long ll;

const int maxn = 2000000;

using namespace std;

inline int read()

while (isdigit(c))

return x * w;

}int n, m, bel[maxn + 5];

namespace graph

edge(int _to, int _nxt) : to(_to), nxt(_nxt) {}

} edge[maxm + 5], redge[maxm + 5];

int tot, head[maxn + 5], rtot, rhead[maxn + 5];

inline void addedge(int u, int v)

inline void init()

vector < int > vec;

bool vis[maxn + 5];

void dfs1(int u)

vec.pb(u);

}void dfs2(int u, int cnt)

}void kosaraju()

memset(vis, false, sizeof(vis));

int cnt = 0;

for (int i = vec.size() - 1; ~i; --i) }}

void init() else if (a && !b) else if (!a && b) else

}}int main()

} puts("possible");

for (int i = 1; i <= n; ++i)

return 0;

}

題意
每個變數有兩種取值,所以問題就是求解乙個\(2-sat\)

題解

\(\begin

a'->b, b'->a & a:h,b:h\\

a'->b', b->a & a:h,b:m \\

a->b, b'->a' & a:m,b:h \\

a->b', b->a' & a:m,b:m

\end\)

只要看有沒有\(x\)和\(x'\)在同乙個強聯通分量即可。

**

// author: 23forever

#include typedef long long ll;

const int maxn = 200;

using namespace std;

inline int read()

while (isdigit(c))

return x * w;

}int n, m, bel[maxn + 5], scc_num;

namespace graph

edge(int _to, int _nxt) : to(_to), nxt(_nxt) {}

} edge[maxm + 5];

int tot, head[maxn + 5];

inline void addedge(int u, int v)

inline void init()

int dfn[maxn + 5], low[maxn + 5], idx;

bool in_sta[maxn + 5];

stack < int > sta;

void dfs(int u) else if (in_sta[v])

} if (dfn[u] == low[u]) while (k != u);

}} void tarjan() }}

void init() else if (c1 == 'h' && c2 == 'm') else if (c1 == 'm' && c2 == 'h') else

}}int main()

}if (!f) puts("good");

} return 0;

}

求解\(2-sat\)問題其實是通過巧妙的建圖方法來解決。這種能推出就拉邊的思想十分常見。在並查集相關題目中也能見到。值得積累。

2 SAT學習筆記

由對稱性解2 sat問題 2 sat解法 上面兩篇 很清楚的介紹了什麼是2 sat以及一些原理演算法 2 sat問題是圖論中乙個比較有意思的問題,重點是建圖,對於邊的意思,就是如果你選了i,就必須選j。2 sat問題有個很明顯的地方就是對於每個i,i包含兩個點,i表示選第乙個點,i 表示選第二個點,...

2 SAT學習筆記

2 sat問題指的是,給你若干個0 1變數。並給你一些限制,讓你求滿足這些限制的可行解 字典序最小的解 限制的種類包括以下幾種 1 a一定是1 2 a一定不是1 3 a,b至少有1個是1 4 a,b最多有乙個是1 5 a,b一定相同 6 a,b一定不同 一般思路是對於乙個變數,建立兩個變數 正,反 ...

2 sat學習筆記

例 struct twosat x xval or y yval void add clause int x,int xv,int y,int yv void init int n bool solve return1 為什麼不需要回溯呢?因為以前定下的變數如果在一輪dfs完之後沒有判為無解,那麼以...