初探2-sat
算法简述
每个元素仅存在选与不选两种状态,且元素间存在某些限制关系,称作\(2-sat\)问题。
算法流程
我们将每个元素\(x\)拆成两个点\(x\)和\(x + n\),分别表示是否选择点\(x\)。最终\(x\)和\(x + n\)中必须选择1个。
将限制关系在其上表达。原命题和逆否命题是同时成立的,应同时在图上表达。
例如限制关系“\(x\)和\(y\)不能同时取”,等价于若选\(x\)则必不能选\(y\),若选\(y\)则不能选\(x\)。故连边\(x \rightarrow y + n\) ,\(y \rightarrow x + n\),有向边\(a \rightarrow b\)表示若选\(a\)则必选\(b\)。
练练手,限制关系"\(x\)和\(y\)不能同时不取",等价于若不选\(x\)则必选\(y\),若不选\(y\)则必选\(x\),故连边\(x + n \rightarrow y , y + n \rightarrow x\)。
再练练手,限制关系“\(x\)和\(y\)要么同时取,要么同时不取”,等价于若选\(x\)则必选\(y\),若不选\(y\)则必不选\(x\),若不选\(x\)则必不选\(y\),若选\(y\)则必选\(x\)。故连边\(x \rightarrow y,y + n \rightarrow x + n,x + n \rightarrow y + n,y\rightarrow x\)。
划重点:有向边\(a \rightarrow b\)表示\(a\)所代表的状态与\(b\)所代表的状态同时成立。
现在我们能够表达出题目中的所有限制关系,现在我们要判断是否有可行解,以及找出某一合法的方案
存在可行解,当且仅当满足题目中所有限制的时候,每个点的状态不存在自相矛盾的情况。若存在点\(x\),状态\(x\)能到达状态\(x + n\)且\(x + n\)能到达\(x\)时,一定会自相矛盾。由此可知,若存在点\(x\),\(x\)与\(x + n\)属于同一个强联通分量时,不存在合法解。
现在我们要打印合法方案。
首先考虑\(Tarjan\)算法缩点,它的意义在于,同1强联通分量内的状态均可相互推出,要么同时成立,要么同时不成立,因而可以看作1个点;同时,缩点以后我们可以得到1个有向无环图。
然后考虑方案的确定。若确定新点\(x\)被选,则意味着新点\(x\)可达的点必须被选,同时意味着\(x\)的对立新点不可被选。所谓的对立新点指的是,同1个点的两个状态分布在了这两个新点中,则这两个新点只能恰好选择1个。
如果我们率先确定出度大于0的新点是否被选,可能会导致后面出现矛盾状况;但如果我们率先确定出度为0的新点\(x\)被选,它唯一的影响是使得它的对立新点\(opp[x]\)不可被选。由此可以考虑这样1个解法:每次找到1个出度为0的新点,标记它和它的对立新点的状态,并将指向它的边收回。直到完成所有标记。
一般来说,我们容易在有向图中将出边收回。不妨建立反图,则等价于执行拓扑排序。
拓扑排序的过程,实际上是对于一对对立节点,我们直接确定了拓扑序较小的那一个的状态。假设我们的规则是令拓扑序较小的节点为选择,那么规则永远是对立节点中拓扑序小的那一个被选,小的不被选。问题转化为,我们只需知道两点拓扑序的相对大小。在\(Tarjan\)算法的\(dfs\)决定了我们的编号顺序与反图的拓扑序的大小关系一致。故只需要比较\(scc\)的编号即可,代码得到进一步简化。
例题
[代码见此](https://github.com/littlewyy/OI/blob/master/AcW 371.cpp)