把 简书 的文章转过来,这边看的人应该多点。
舞蹈链(Dancing links)实际上是一种数据结构,可以用来实现 X算法,以解决精确覆盖问题。
什么是精确覆盖(Exact Cover)问题呢?维基百科上对精确覆盖的定义如下:在一个全集 X 中若干子集的集合为 S。S* 是 S 的一个子集,当且仅当 X 中的每一个元素在 S* 中恰好出现一次时,S* 称之为一个精确覆盖。在计算机科学中,精确覆盖问题指找出这样的一种覆盖,或证明其不存在。这是一个NP-完全问题。
例如,S = {A,B,C,D,E,F} 是全集 X = {1,2,3,4,5,6,7} 的一个子集的集合,其中:
A = {1, 4, 7}
B = {1, 4}
C = {4, 5, 7}
D = {3, 5, 6}
E = {2, 3, 6, 7}
F = {2, 7}
那么,S 的一个子集 S* = {B, D, F} 是 X 的一个精确覆盖,因为 X 中的每个元素恰好在 S* 中出现了一次。
可以用 0-1 矩阵来表示精确覆盖问题。我们用矩阵的每行表示 S 的一个元素,也就是 X 的一个子集;用矩阵的每列表示 X 的一个元素。矩阵中的 1 代表这一列的元素存在于这一行对应的子集中,0 代表不存在。那么精确覆盖问题可以转化成求出矩阵若干行的集合,使得集合中的每一列恰好都有一个 1。
比如前面的问题可以用矩阵的形式表示成
1 2 3 4 5 6 7 A 1 0 0 1 0 0 1 B 1 0 0 1 0 0 0 C 0 0 0 1 1 0 1 D 0 0 1 0 1 1 0 E 0 1 1 0 0 1 1 F 0 1 0 0 0 0 1 \begin{array} {c|ccccccc} & 1 & 2 & 3 & 4 & 5 & 6 & 7 \\ \hline A & 1 & 0 & 0 & 1 & 0 & 0 & 1 \\ \color{#d00}{B} & \color{#d00}{1} & \color{#d00}{0} & \color{#d00}{0} & \color{#d00}{1} & \color{#d00}{0} & \color{#d00}{0} & \color{#d00}{0} \\ C & 0 & 0 & 0 & 1 & 1 & 0 & 1 \\ \color{#d00}{D} & \color{#d00}{0} & \color{#d00}{0} & \color{#d00}{1} & \color{#d00}{0} & \color{#d00}{1} & \color{#d00}{1} & \color{#d00}{0} \\ E & 0 & 1 & 1 & 0 & 0 & 1 & 1 \\ \color{#d00}{F} & \color{#d00}{0} & \color{#d00}{1} & \color{#d00}{0} & \color{#d00}{0} & \color{#d00}{0} & \color{#d00}{0} & \color{#d00}{1} \end{array} ABCDEF1110000200001130001104111000500110060001107101011
那么选择红色的 B,D,F 能满足每列都恰好包含一个 1。
可以用 Knuth 提出的 X算法 来解决精确覆盖问题。X算法是一个非确定性的深度优先回溯算法。它的具体步骤如下:
- 如果矩阵 A A A为空(没有任何列),则当前局部解即为问题的一个解,返回成功;否则继续。
- 根据一定方法选择第 c 列。如果某一列中没有 1,则返回失败,并去除当前局部解中最新加入的行。
- 选择第 r 行,使得 A r , c A_{r, c} Ar,c = 1(该步是非确定性的)。
- 将第 r 行加入当前局部解中。
- 对于满足 A r , j A_{r, j} Ar,j = 1的每一列j,从矩阵 A A A中删除所有满足 A i , j A_{i, j} Ai,j = 1的行,最后再删除第 j 列。
- 对所得比 A 小的新矩阵递归地执行此算法。
让我们用 X算法 解决上面的精确覆盖问题。
首先,当前矩阵不为空,算法继续进行。那么先选择 1 最少的一列。因为 1,2,3,5,6 列都只有 2 个 1,因此我们随便选择 1 个,比如第 1 列。
1 2 3 4 5 6 7 A 1 0 0 1 0 0 1 B 1 0 0 1 0 0 0 C 0 0 0 1 1 0 1 D 0 0 1 0 1 1 0 E 0 1 1 0 0 1 1 F 0 1 0 0 0 0 1 \begin{array}{c|ccccccc} & \color{#d00}{1} & 2 & 3 & 4 & 5 & 6 & 7 \\ \hline A & \color{#d00}{1} & 0 & 0 & 1 & 0 & 0 & 1 \\ B & \color{#d00}{1} & 0 & 0 & 1 & 0 & 0 & 0 \\ C & 0 & 0 & 0 & 1 & 1 & 0 & 1 \\ D & 0 & 0 & 1 & 0 & 1 & 1 & 0 \\ E & 0 & 1 & 1 & 0 & 0 & 1 & 1 \\ F & 0 & 1 & 0 & 0 & 0 & 0 & 1 \end{array} ABCDEF11100002000011