2-SAT
前置知识:有向图 Tarjan
k-SAT 问题
SAT 是适定性(Satisfiability)问题的简称。
k-SAT 问题就是给定
2-SAT 建图
2-SAT 建图用到了种类并查集的思想,将每个点
以
另一个例子:
2-SAT 建图的对称性:
对于
void add(int x,int y){ne[++t]=he[x],to[t]=y,he[x]=t;}
void ins(int x,int y){add(x,y),add(y>n?y-n:y+n,x>n?x-n:x+n);}
Tarjan 求解 2-SAT
2-SAT 问题通常转化为强连通分量问题,用 Tarjan 求解。
用 Tarjan 求强连通分量,若
构造方法:
Tarjan 的拓扑序是逆序,即从后求出的强连通分量可能到达先求出的强连通分量,从先求出的不可能到达后求出的。若
正确性证明:
根据以上构造过程,
对于任意
模板题:P4782 【模板】2-SAT 问题
AC 代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+3,M=4e6+3;
int he[N],to[M],ne[M],n,t,l[N],id,st[N],tp,bl[N],ct;
void add(int x,int y){ne[++t]=he[x],to[t]=y,he[x]=t;}
void ins(int x,int y){add(x,y),add(y>n?y-n:y+n,x>n?x-n:x+n);}
void tar(int x){
int p=++id,i=he[x],j;
for(l[st[++tp]=x]=p;i;i=ne[i])if(!l[j=to[i]])tar(j),l[x]=min(l[x],l[j]);else if(!bl[j])l[x]=min(l[x],l[j]);
if(l[x]==p)for(++ct;bl[st[tp]]=ct,st[tp--]!=x;);
}
int main(){
int m,i,j,u,v;
scanf("%d%d",&n,&m);
while(m--)scanf("%d%d%d%d",&i,&u,&j,&v),ins(u?i+n:i,v?j:j+n);
for(i=1;i<=n*2;++i)if(!l[i])tar(i);
for(i=1;i<=n;++i)if(bl[i]==bl[i+n])return puts("IMPOSSIBLE"),0;
puts("POSSIBLE");
for(i=1;i<=n;++i)printf("%d ",bl[i]<bl[i+n]);
return 0;
}
习题(按难度排序):
P4171 [JSOI2010]满汉全席
P5782 [POI2001]和平委员会
P3825 [NOI2017]游戏
P6378 [PA2010]Riddle(前缀优化建图)
CF1215F Radio Stations(前缀优化建图)
CF587D Duff in Mafia(前缀优化建图)
P6965 [NEERC2016]Binary Code(前缀优化建图)
CF1007D Ants(树剖+线段树+前缀优化建图)题解
DFS 求解 2-SAT
适用于某些有特殊限制,需要按特定顺序选点的 2-SAT 问题。时间复杂度最坏
算法流程:从某个点开始 dfs,将经过的点
代码(模板题数据较水,截止本博客发表此代码可 AC):
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+3,M=4e6+3;
int he[N],to[M],ne[M],n,t;
bool b[N];
void add(int x,int y){ne[++t]=he[x],to[t]=y,he[x]=t;}
void ins(int x,int y){add(x,y),add(y>n?y-n:y+n,x>n?x-n:x+n);}
bool dfs(int x){
if(b[x>n?x-n:x+n])return 0;//搜索失败
if(b[x])return 1;//搜过了就不用再搜了
b[x]=1;//打标记
for(int i=he[x];i;i=ne[i])if(!b[to[i]]&&!dfs(to[i]))return b[x]=0;//失败并清空标记
return 1;//成功
}
int main(){
int m,i,j,u,v;
scanf("%d%d",&n,&m);
while(m--)scanf("%d%d%d%d",&i,&u,&j,&v),ins(u?i+n:i,v?j:j+n);
for(i=1;i<=n;++i)if(!b[i]&&!b[i+n]&&!dfs(i)&&!dfs(i+n))return puts("IMPOSSIBLE"),0;//若 i 和 i+n 都搜索失败则无解
puts("POSSIBLE");
for(i=1;i<=n;++i)printf("%d ",b[i]);
return 0;
}
习题:
P3007 [USACO11JAN]The Continental Cowngress G
CF568C New Language(字典序贪心)
P5332 [JSOI2019]精准预测(bitset 优化,图是 DAG 所以不需要跑 Tarjan)
参考资料:OI Wiki 2-SAT