首页 > 技术文章 > 2-SAT超入门讲解

cjjsb 2018-10-11 13:21 原文

Preface

说实话2-SAT的题目我都没怎么做过,所以这里讲的都是些超入门什么的

还有一些板子题由于是暑假的时候学的所以有些我也记不清了

主要学习参考自:Manchery的课件&&dalao's blog&&Another dalao


What is 2_SAT?

SAT是适定性(Satisfiability)问题的简称 。一般形式为k-适定性问题,简称 k-SAT。

可以证明,当\(k>2\)时,k-SAT是NP完全的。因此一般讨论的是\(k=2\)的情况,即2-SAT问题。

我们通俗的说,就是给你\(n\)个变量\(a_i\),每个变量能且只能\(0/1\)的值。同时给出若干条件,形式诸如\((not)a_i\operatorname{opt}(not)\ a_j=0/1\),其中\(opt\)表示\(and,or,xor\)中的一种

而求解2-SAT的解就是求出满足所有限制的一组\(a\)


Change 2-SAT into Graph Theory

首先我们考虑将2-SAT问题往图论的方向靠,我们发现每个点要么取\(0\),要么取\(1\)。因此对于\(a_i\),我们建两个点\(2i-1\)\(2i\)分别表示\(a_i\)\(0\)\(1\)

然后我们考虑建边来表示这些关系,我们令一条有向边的意义:\(x\to y\)表示如果选择了\(x\)就必须选\(y\)

那么我们可以举一些简单的例子来总结下连边的规律(用\(i'\)表示\(i\)的反面):

  • \(i,j\)不能同时选:选了\(i\)就要选\(j'\),选\(j\)就要选\(i'\)。故\(i\to j',j\to i'\)。一般操作即为\(a_i \operatorname{xor} a_j=1\)
  • \(i,j\)必须同时选:选了\(i\)就要选\(j\),选\(j\)就要选\(i\)。故\(i\to j,j\to i\)。一般操作即为\(a_i \operatorname{xor} a_j=0\)
  • \(i,j\)任选(但至少选一个)选一个:选了\(i\)就要选\(j'\),选\(j\)就要选\(i'\),选\(i'\)就要选\(j\),选\(j'\)就要选\(i\)。故\(i\to j',j\to i',i'\to j,j'\to i\)。一般操作即为\(a_i \operatorname{or} a_j=1\)
  • \(i\)必须选:直接\(i'\to i\),可以保证无论怎样都选\(i\)。一般操作为给出的\(a_i=1\)\(a_i \operatorname{and} a_j=1\)

建好图然后就是考虑怎么用图论的方式解决2-SAT了。


How to solve 2-SAT——DFS

  1. 对于每个当前不确定的变量\(a_i\),令\(a_i=0\)然后沿着边DFS访问相连的点。
  2. 检查如果会导致任意一个\(j\)\(j'\)都被选,那么撤销。否则令\(a_i=0\)
  3. 否则令\(a_i=1\),重复2。如果还不行就无解。
  4. 继续考虑下一个不确定的变量

这样的话正确性显然,由于这里的DFS涉及到全局,因此复杂度是\(O(n(n+m))\)的。

一般情况下已经很优秀了,而且还可以改进:

只需要在DFS之前判断\(i'\)能否走到\(i\)就可以省略撤销标记的过程,所以我们可以bitset优化传递闭包做到\(O(\frac{n^3}{w})\)预处理,然后就可以\(O(n+m)\)的DFS了。

这种做法还可以保证解的字典序,有时不失为一种不错的方法。


How to solve 2-SAT——SCC

考虑我们上面的判断有无解的情况,我们想到完全可以借助SCC来判断两个点是否互相到达。

那么我们先缩点,如果\(i\)\(i'\)在同一SCC里那么显然无解。

否则选择\(i\)\(i'\)拓扑序较大的一个就可以得到一组可行解。

不是很常用(主要是一般题目的数据范围都不需要),用来判可行性比较好。


两道例题

HDU3062&&HDU1814 sol


Postscript

这真的是一篇超入门博客,没有涉及特别多的难点以及姿势。

数据结构优化建图我是肯定不会的啦,最后推荐一道比较有难度的2-SAT好题:Luogu P3825 [NOI2017]游戏&&Sol

推荐阅读