问题描述
- n个布尔变量xi,另有m个需要满足的条件,每个条件的形式都是“xi为真/假或者xj为真/假”。比如:“x1为真或者x3为假”;“或”指的是两个条件至少有一个正确,如上述条件共有三种合法情况:x1真x3真,x1真x3假,x1假x3假。2-SAT问题的目标是给每个变量赋值,使所有条件得到满足。
蓝书解法
- 构造一张有向图G,把其中的每个变量拆成两个结点2i和2i+1,分别表示xi为假和xi为真,最后要为每个变量选择其中的一个结点打标记。对于“xi为假或者xj为假”这样一个条件,我们连一条有向边2i+1=>2j,表示xi为真时xj必须为假,同理,还需要连一条有向边2j+1=>2i;其它条件类似,每个条件对应两条“对称”的边(若某个变量的取值提前确定,则整个图不对称)。整张图实际上描述了一系列必须满足的关系,选择u的情况下必须选择它所能到达的所有点。
- 对于一个没有打标记的变量xi,我们先假定它为假,然后标记结点2i,并且沿着有向边标记所有能标记的结点。如果标记过程中发现某个变量对应的两个结点都被标记,则“xi为假”这个假定不成立,需要改成“xi为真”,然后重新标记。整个算法没有回溯过程,如果当前考虑的变量不管赋值为真还是赋值为假都会引起矛盾,可以证明整个2-SAT问题无解(调整以前赋值的其它变量也没用)。
蓝书代码+注释
int n,last[maxn*2],s[maxn*2],c,cnt;//s用来存解决方案
bool mark[maxn*2];//打标记 ,i假,i+1真
struct edge{
int v,next;
}e[maxm*2];
inline void add(int u,int u0,int v,int v0)
{
u=u*2+u0;//满足改条件变量u对应的结点
v=v*2+v0;
e[++cnt].next=last[u];
e[cnt].v=v^1;//根据必须满足的关系建图
last[u]=cnt;
e[++cnt].next=last[v];
e[cnt].v=u^1;
last[v]=cnt;
}
bool dfs(int x)//对x进行dfs,意味着当前要标记x
{
if(mark[x^1]) return false;//
if(mark[x]) return true;//x被标记过,意味x连出去的一系列条件得到了满足
mark[x]=true;
s[c++]=x;//相当于滚动数组,仅用来存当前dfs标记
for(int i=last[x];i;i=e[i].next)
{
int v=e[i].v;
if(!dfs(v)) return false;
}
return true;
}
bool solve()//对2-SAT问题进行求解
{
for(int i=0;i<n*2;i+=2)//点的编号从0开始方便用位运算优化
{
if(!mark[i]&&!mark[i^1])
{
c=0;
if(!dfs(i))//标记为假不行时
{
while(c>0) mark[s[--c]]=false;//清空标记
if(!dfs(i^1)) return false;
}
}
}
return true;
}
关于dfs的几点证明
1.一次假设(对xi的dfs)不成立,只能是出现环且该环中存在同一变量两个对应点的情况,否则由于图的严格对称性,!xi会在此之前被标记,就不会对xi进行dfs了!同时也说明了为什么当前变量两种赋值都不行时调整其它变量也没用。
2.每次dfs时初始c=0,滚动数组保存当次dfs的标记。常数小于用queue维护。
3.输出方案只需要询问mark即可。
Tarjan解法
-
同样可以用tarjan解决这类问题,每个点拆成两个点表示两种抉择,对于前面的二元关系可以以同样的方式连边得到G,这样这张图的含义同上,具有严格的对称性,然后tarjan缩点(缩小图的规模,便于求解)得到G1,若同一变量的两个状态存在于同一边的强联通分量中,则无解,反之则将缩点后的图每条边取反得到G2,按照拓扑序重复一下操作:
1.选择第一个未着色的顶点 x 把x染成红色。
2.把所有与x矛盾的点y(存在bj∈x代表的强连通分量,!bj∈y所代表的强连通分量,则x与y相互矛盾,y及其y的子孙都应该被染成蓝色)。
3.重复操作1、2,直到不存在未着色点为止,G2中染成红色的强连通分量在G中对应的点为一组解。
4.整个算法的时间复杂度为O(e)。
-Tarjan的一些证明
0.由于缩点前图的对称性(若xi与yi在同一强连通分量,!xi与!yi也在同一强连通分量),xi与!xi间不存在直接通路。
1.上述染色操作不会同时将xi和!xi染成红色(染红时接着将所有的矛盾点及其子孙染蓝)。
2.我们得到的解对于xi与!xi会且仅会包含其中的一个。假设在一次染色中全部染成蓝色,必然会有以下情况:
因此不存在此情况。假设在两次染色中被染成蓝色,则有形如以下情况:
由于染色按照拓扑序,p,q不可能被染成红色。综上反证出我们得到的解对于xi与!xi会且仅会包含其中的一个。
- 对于一元关系(xi与!xi间连有向边),由于染色时严格按照拓扑序,实际也不会出现两个点同蓝或同红。
- Tarjan缩点模板
- 另外,针对2-SAT问题所建的图描述的是限制关系,因此解决问题时要将限制条件精确表达。