2-SAT问题:有n个bool变量x1,x2....xn,还有一些必须满足的条件。这些条件列如“x1为假或x2为真”,即“x1假x2假”、“x1假x2真”、“x1真x2真”。
求解2-SAT问题的算法有很多,有一种效率还不错,实现方便又理解方便的算法。
注意例子中的那个条件“x1为假或x2为真”,在x1为假的时候x2可以为任意值,但在x1为真的时候,x2为满足条件必须是真。也就是说x1为真可以推导出x2为假、x2为假可以推导出x1为真。换成一般的情况,已知xa→xb',则xb→xa'、已知xa'→xb,则xb'→xa(xi表示真xi'表示假)这个称为对称性,仔细理解下,下面证明会用到这个结论。
我们通常建一个图G,图中节点数量为2n,把节点标号,2i节点代表着xi、2i+1代表xi'。如果xi为真的时候标记节点2i,为假的时候标记2i+1。如果知道xa→xb',则加入一条有向边2a→2b+1,同时得到xb→xa',再加入一条边2b→2a+1。
枚举2i和2i+1都没有标记的节点,先假设xi为真,给2i标记,再进行dfs遍历,把所有可以到达的点都标记,标记过程中如果出现某个点2x和2x+1都被标记,因为不会存在x同时为真和假的情况,所以xi不能为真。再假设x2为假,给2i+1标记重复上述过程。如果xi值为真和假时都不可以,则此题无解。
修改之前的变量可以不可以让正在枚举的点可以为真或假?答案是不可以。因为假设某个节点a被标记,dfs时遍历到a'发现矛盾,则有条路可以从i走到a’,根据对称性,a有条路可以走到i,i就不是2i和2i+1都没有被标记的节点,矛盾,所以不存在这种情况。所以dfs遍历到的点要么是已经标记过的点或者2i和2i+1都未标记的点,与之前的变量无关,所以2-SAT算法正确。
伪代码:
bool dfs(int x){
____如果x节点被标记,返回真
____如果x^1节点被标记,返回假
____u为x可以到达的所有节点
________如果dfs(u)为假,返回假
____返回真
}
bool solve(){
____for i=1 to n
________如果2i和2i+1都为标记
____________如果dfs(2i)为假
________________清除上次遍历的节点的标记 //实现时可以用栈
________________如果dfs(2i+1)为假,返回假
____返回真
}