2-SAT总结

2-SAT 问题】
现有一个由 N 个布尔值组成的序列 A ,给出一些限制关系,比如 A[x] AND A[y]=0 A[x] OR A[y] OR A[z]=1 等,要确定 A[0..N-1] 的值,使得其满足所有限制关系。这个称为 SAT 问题,特别的,若每种限制关系中最多只对两个元素进行限制,则称为 2-SAT 问题。

由于在 2-SAT 问题中,最多只对两个元素进行限制,所以可能的限制关系共有 11 种:
A[x]
NOT A[x]
A[x] AND A[y]
A[x] AND NOT A[y]
A[x] OR A[y]
A[x] OR NOT A[y]
NOT (A[x] AND A[y])
NOT (A[x] OR A[y])
A[x] XOR A[y]
NOT (A[x] XOR A[y])
A[x] XOR NOT A[y]
进一步, A[x] AND A[y] 相当于 (A[x]) AND (A[y]) (也就是可以拆分成 A[x] A[y] 两个限制关系), NOT(A[x] OR A[y]) 相当于 NOT A[x] AND NOT A[y] (也就是可以拆分成 NOT A[x] NOT A[y] 两个限制关系)。因此,可能的限制关系最多只有 9 种。

在实际问题中, 2-SAT 问题在大多数时候表现成以下形式:有 N 对物品,每对物品中必须选取一个,也只能选取一个,并且它们之间存在某些限制关系(如某两个物品不能都选,某两个物品不能都不选,某两个物品必须且只能选一个,某个物品必选)等,这时,可以将每对物品当成一个布尔值(选取第一个物品相当于 0 ,选取第二个相当于 1 ),如果所有的限制关系最多只对两个物品进行限制,则它们都可以转化成 9 种基本限制关系,从而转化为 2-SAT 模型。

【建模】
其实 2-SAT 问题的建模是和实际问题非常相似的。
建立一个 2N 阶的有向图,其中的点分为 N 对,每对点表示布尔序列 A 的一个元素的 0 1 取值(以下将代表 A[i] 0 取值的点称为 i ,代表 A[i] 1 取值的点称为 i' )。显然每对点必须且只能选取一个。然后,图中的边具有特定含义。若图中存在边 <i, j> ,则表示若选了 i 必须选 j 。可以发现,上面的 9 种限制关系中,后 7 种二元限制关系都可以用连边实现,比如 NOT(A[x] AND A[y]) 需要连两条边 <x, y'> <y, x'> A[x] OR A[y] 需要连两条边 <x', y> <y', x> 。而前两种一元关系,对于 A[x] (即 x 必选),可以通过连边 <x', x> 来实现,而 NOT A[x] (即 x 不能选),可以通过连边 <x, x'> 来实现。

O(NM) 算法:求字典序最小的解】
根据 2-SAT 建成的图中边的定义可以发现,若图中 i j 有路径,则若 i 选,则 j 也要选;或者说,若 j 不选,则 i 也不能选;
因此得到一个很直观的算法:
1 )给每个点设置一个状态 V V=0 表示未确定, V=1 表示确定选取, V=2 表示确定不选取。称一个点是已确定的当且仅当其 V 值非 0 。设立两个队列 Q1 Q2 ,分别存放本次尝试选取的点的编号和尝试不选的点的编号。
2 )若图中所有的点均已确定,则找到一组解,结束,否则,将 Q1 Q2 清空,并任选一个未确定的点 i ,将 i 加入队列 Q1 ,将 i' 加入队列 Q2
3 )找到 i 的所有后继。对于后继 j ,若 j 未确定,则将 j 加入队列 Q1 ;若 j' (这里的 j' 是指与 j 在同一对的另一个点)未确定,则将 j' 加入队列 Q2
4 )遍历 Q2 中的每个点,找到该点的所有前趋(这里需要先建一个补图),若该前趋未确定,则将其加入队列 Q2
5 )在( 3 )( 4 )步操作中,出现以下情况之一,则本次尝试失败,否则本次尝试成功:
<1>
某个已被加入队列 Q1 的点被加入队列 Q2
<2>
某个已被加入队列 Q2 的点被加入队列 Q1;
<3>
某个 j 的状态为 2
<4>
某个 i' j' 的状态为 1 或某个 i' j' 的前趋的状态为 1
6 )若本次尝试成功,则将 Q1 中的所有点的状态改为 1 ,将 Q2 中所有点的状态改为 2 ,转( 2 ),否则尝试点 i' ,若仍失败则问题无解。
该算法的时间复杂度为 O(NM) (最坏情况下要尝试所有的点,每次尝试要遍历所有的边),但是在多数情况下,远远达不到这个上界。
具体实现时,可以用一个数组 vst 来表示队列 Q1 Q2 。设立两个标志变量 i1 i2 (要求对于不同的 i i1 i2 均不同,这样可以避免每次尝试都要初始化一次,节省时间),若 vst[i]=i1 则表示 i 已被加入 Q1 ,若 vst[i]=i2 则表示 i 已被加入 Q2 。不过 Q1 Q2 仍然是要设立的,因为遍历( BFS )的时候需要队列,为了防止重复遍历,加入 Q1 (或 Q2 )中的点的 vst 值必然不等于 i1 (或 i2 )。中间一旦发生矛盾,立即中止尝试,宣告失败。

该算法虽然在多数情况下时间复杂度到不了 O(NM) ,但是综合性能仍然不如下面的 O(M) 算法。不过,该算法有一个很重要的用处:求字典序最小的解!
如果原图中的同一对点编号都是连续的( 01 23 45…… )则可以依次尝试第 0 对、第 1 …… 点,每对点中先尝试编号小的,若失败再尝试编号大的。这样一定能求出字典序最小的解(如果有解的话),因为一个点一旦被确定,则不可更改
如果原图中的同一对点编号不连续(比如 03 25 14…… )则按照该对点中编号小的点的编号递增顺序将每对点排序,然后依次扫描排序后的每对点,先尝试其编号小的点,若成功则将这个点选上,否则尝试编号大的点,若成功则选上,否则(都失败)无解。

你可能感兴趣的:(2-SAT总结)