定义
2-SAT问题描述如下:有n个集合,每个集合里有两个元素且必须选一个,再给出若干条限制条件,比如选了i元素就不能j元素,求可行的选择方案。
ps:其实有k-SAT(k>=3)问题,但已经被证明是NPC问题,所以无视~
分析
首先我们要把给出的限制分析清楚。由于每个集合里有两个元素,所以如果某一个元素不能选,另一个就必须选。假设i集合的元素分别为2*(i-1),2*(i-1)+1(这两个元素可以相互异或1得到,下文用^代表异或),则如果选i不能选j,我们需要建两条边:i->j^1,j->i^1。
这里的建边表示选择,也就是说i->j如果有边,说明选i,就必选j。
解法1
对于一个没有元素被选的集合,我们可以先选一个点,然后去验证选该点是否满足限制,如果不满足,就换另外一个点,再去验证,还不满足说明此2-SAT无解。
这个解法很暴力,看起来也像是正确的,但再深究一下就会感觉不是很对:为什么不可以在均不满足限制的情况下,去更改前面一个集合的选项,从而满足限制呢?
其实原因很简单:由于集合i还没有确定选什么,所以集合i与集合1~i-1是无关的(有关的话肯定被确定了)。由于无关,所以前面怎么更改都没有作用。
复杂度:
O(n∗m)
好处:效率并不是很糟糕,而且由于是暴力枚举的,所以可以求一些特殊解(比如字典序),而且可以处理不对称(下面会讲到)的情况。
解法2
我们会发现,一个强连通分量中的点一旦选了一个,整个强连通分量就必须全部选择,那么我们可以用Tarjan找到所有极大强连通分量。所以,如果一个集合中的两个元素在同一个SCC(强连通分量)中,就是无解的。但是怎么说明没有上述情况出现时,问题必定有解,又怎么找到解呢?
我们将新图的反图进行拓扑排序,就可以得到反图中每个点所在SCC的拓扑序。看图(注意这是新图不是反图):
由此可见,如果反图中i所在SCC的拓扑序>反图中i^1所在SCC的拓扑序且存在路径使i->i^1,我们就不能够选i,因为这将导致i^1被选。所以在i和i^1中我们需要选择反图拓扑序较小的元素(如果i和i^1之间没有路径显然可以随意选)。这样可以求出一组任意解,但显然不能求出字典序最小(大)的解。
前面一直说着反图反图,但实际上并不需要真的去重新建图,因为我们关注的是拓扑序,而反图只不过是拓扑序反一下的事情(多谢Manchery大佬指导Orz)。我们还可以惊奇的发现,反图的拓扑序就是Tarjan生成SCC的顺序(又谢Manchery大佬指导%%%),这个yy一下很好得出:由于Tarjan会递归调用下去,所以反图拓扑序小的SCC会优先被处理出来。有了这些发现,代码实现就变得非常简单啦。
这种想法看似天衣无缝,但实际上一定要在2-SAT满足对称性的情况下才正确。
对称性:如果i能够到j,那么j^1就能够到i^1。
若2-SAT的限制都是常规限制(选i不能选j),那么该2-SAT肯定满足对称性。这个非常显然就不用解释啦:P。
如果不满足对称性,就可能会出现这种情况导致矛盾:
而在满足对称性的情况下,就不会出现该情况。
(本人太蒟蒻,只知道如何说明对称性的必要,但算法正确性的严格证明并不会,会严格证明的dalao可以给我提些建议,谢谢Orz)
复杂度: O(n+m)
模板题
1.字典序最小解:HDU1814,题解传送门。
2.任意解:POJ3683,题解传送门。
2017.7.28UPD:常用技巧&优化
1.如果某个集合i必选2*(i-1)或必选2*(i-1)+1,那么连2*(i-1)->2*(i-1)+1或2*(i-1)+1->2*(i-1)。这看起来并不对称,但要注意到对称的定义:如果i能够到j,那么j^1就能够到i^1。所以是对称的。
2.线段树优化建图,实际上适合很多图论算法,不仅仅是2-SAT。这里有一道比较简单的题目。
3.前后缀优化建图,这个就2-SAT用的多了。这里有一道比较简单的题目。