Defination
- k-SAT问题 :通俗的可以理解为, 对于
n
n
n个集合,每个集合有
k
k
k个元素,集合内元素只能有一个为
1
1
1,集合的元素间有限制。
-类型: NP - 2-SAT: [ k = = 2 ] [k==2] [k==2],类型:P
Algorithm
1.对每个 i i i,有 i 0 , i 1 i_0,i_1 i0,i1,表示 i i i选0或1。
2.题目的限制( i , j i,j i,j至少一个为1, i , j i,j i,j只有一个为1…),转化为 : i k − > j p , j p x o r 1 − > i k x o r 1 i_k- > j_p,j_{p\ xor\ 1}->i_{k\ xor\ 1} ik−>jp,jp xor 1−>ik xor 1。
3.对 2 ∗ n 2*n 2∗n个点建有向图,跑 t a r j a n tarjan tarjan强连通分量缩点。
4.若 ∃ i 0 , i 1 \exist\ i_0,i_1 ∃ i0,i1在同一强连通分量,则原问题无解。
5.否则,原问题有解(解的数目不确定)
6.证明有解: 直接从构造方式证明:
∀
i
,
{
c
h
o
o
s
e
i
0
d
f
n
[
i
0
]
<
d
f
n
[
i
1
]
c
h
o
o
s
e
i
1
d
f
n
[
i
1
]
<
d
f
n
[
i
0
]
\forall \ i, \begin{cases} choose\ i_0 & dfn[i_0]<dfn[i_1] \\ choose\ i_1 & dfn[i_1]<dfn[i_0] \\ \end{cases}
∀ i,{choose i0choose i1dfn[i0]<dfn[i1]dfn[i1]<dfn[i0]
-6.1
i
i
i自身不矛盾。
-6.2
i
i
i不会让
j
j
j矛盾。
若
i
k
−
>
j
k
−
>
.
.
.
−
>
j
k
x
o
r
1
i_k->j_k->...->j_{k\ xor\ 1}
ik−>jk−>...−>jk xor 1,根据建图的对称性(
i
k
−
>
j
p
,
j
p
x
o
r
1
−
>
i
k
x
o
r
1
i_k- > j_p,j_{p\ xor\ 1}->i_{k\ xor\ 1}
ik−>jp,jp xor 1−>ik xor 1 ),有
j
k
x
o
r
1
−
>
i
k
j_{k\ xor\ 1}->i_k
jk xor 1−>ik,矛盾。
-6.3 若 i k ! − > i k x o r 1 & & i k x o r 1 ! − > i k i_k \ !->\ i_{k\ xor\ 1} \And\And\ i_{k\ xor\ 1}\ !->i_k ik !−> ik xor 1&& ik xor 1 !−>ik,任选其一。(10.20)
-6.4解的数量是: 2 c n t 自 由 元 2^{cnt_{自由元}} 2cnt自由元 。(10.20)
Application
1.找到 两 元 性 两元性 两元性,即 对于每一个元素,两种状态有且仅有一种成立。
2.构造元素间约束关系。
EXAMPLES
1.WOJ#4586 逻辑命题
-
每个命题 t r u e / f l a s e true/flase true/flase,为2元,转化至少一个成立: 一个成立,另一个一定成立(由对称性,“至少一个成立”转化成 4 条边。但实际上重合了,只有2条边。)
-
直接 2 − s a t 2-sat 2−sat,看 i 0 i_0 i0和 i 1 i_1 i1能否互相推出即可。
#include<bits/stdc++.h>
using namespace std;
#define sf scanf
#define file freopen("data.in","r",stdin);
const int N=1e5+10;
int cnt=0,head[N],nxt[N],to[N],n,m,con[1005][1005];
inline void _add(int u,int v){
nxt[++cnt]=head[u];head[u]=cnt;to[cnt]=v;
}
int dfn[N],low[N],col[N],CN=0,tim=0,zhn[N],top=0,vis[N];
inline int idx(int x){
return x>n? x-n:x+n;
}
bool dfs(int u){
if(col[u]==2)return 1;
col[u]=1;col[idx(u)]=2;
for(int i=head[u];i;i=nxt[i])if(col[to[i]]!=1)if(dfs(to[i]))return 1;
}
int ans[N];
signed main(){
sf("%d%d",&n,&m);
for(int i=1;i<=m;++i){
char a,b;int x,y;
sf("\n%d %c %d %c",&x,&a,&y,&b);
_add(a=='N'? x : idx(x),b=='N'? idx(y) : y);
_add(b=='N'? y : idx(y),a=='N'? idx(x) : x);
}
for(int i=1;i<=n;++i){
int f1=0,f2=0;
memset(col,0,sizeof col);
f1=dfs(i);
memset(col,0,sizeof col);
f2=dfs(idx(i));
if(f1&&f2){
cout<<"IMPOSSIBLE";
return 0;
}
if(!f1&&f2){
ans[i]=1;
}
if(f1&&!f2){
ans[i]=2;
}
if(!f1&&!f2){
ans[i]=3;
}
}
for(int i=1;i<=n;++i){
if(ans[i]==1)cout<<"Y";
if(ans[i]==2)cout<<"N";
if(ans[i]==3)cout<<"?";
}
// bfs();
return 0;
}