abastract:利用dancing links 解决精确覆盖问题,例如数独,n皇后问题。
要学习dacning links 算法,首先要先了解该算法所适用的问题,即精确覆盖问题,下面先了解精确覆盖问题。
精确覆盖问题
何为精确覆盖问题
在一个全集X中若干子集的集合为S,精确覆盖(Exactcover)是指,S的子集S*,满足X中的每一个元素在S*中恰好出现一次。
定义
满足以下条件的集合为一个精确覆盖:
S*中任意两个集合没有交集,即X中的元素在S*中出现最多一次
S*中集合的全集为X,即X中的元素在S*中出现最少一次
合二为一,即X中的元素在S*中出现恰好一次。
举例
令={N,O,E,P}是集合X={1,2,3,4}的一个子集,并满足:
N={}
O={1,3}
E={2,4}
P={2,3}.
其中一个子集{O,E}是X的一个精确覆盖,因为O={1,3}而E={2,4}的并集恰好是X={1,2,3,4}。同理,{N,O,E}也是X.的一个精确覆盖。空集并不影响结论。
精确覆盖问题的表示方式
一般的,我们用一个集合s包含s中的元素的单向关系表示精确覆盖问题。常用的有以下两种方法:
- 矩阵表示法
包含关系可以用一个关系矩阵表示。.矩阵每行表示S的一个子集,每列表示X中的一个元素。矩阵行列交点元素为1表示对应的元素在对应的集合中,不在则为0。
通过这种矩阵表示法,求一个精确覆盖转化为求矩阵的若干个行的集合,使每列有且仅有一个1。同时,该问题也是精确覆盖的典型例题之一。
下表为其中一个例子:
S*={B,D,F}便是一个精确覆盖。
- 图论表示法
可将精确覆盖问题转化为一个二分图,左侧为集合,右侧为元素,左侧集合若与右侧元素有包含关系则连边,通过将左侧节点与其所有边保留与否求解一个右侧的每一个节点恰好有一条边的匹配。
接下来就是dancing links x算法了。
Dancing Links X 算法
历史
X算法是高德纳提出的解决精确覆盖问题的算法,而dancing links X算法则是DonKnuth(《计算机程序设计艺术》的作者)提出的对X算法的一种高效实现,这种实现建立在如上所说的矩阵表示法上。
算法思想
由如上精确覆盖问题的矩阵表示法中,我们知道dancing links x 是用来求解一个 01矩阵中选取哪几行可以使得这几行每一列都有且仅有一个1(就是每个元素在这几个子集里有且仅有出现过一次)。
先不管他的实际意义,我们需要做的就是在一个01矩阵的选取某几行使之符合上述条件。
我们很容易就想到枚举,然后判断符不符合条件,但是这个做法实在是太消耗时间。
dacing links x就是一个高效的求解该类问题的算法,而这种算法,基于交叉十字循环双向链(听起来很高大上,其实就是一堆链表)的数据结构。
例如:如下的矩阵
就包含了这样一个集合(第1、4、5行)
如何利用给定的矩阵求出相应的行的集合呢?我们采用回溯法
先假定选择第1行,如下所示:
如上图中所示,红色的那行是选中的一行,这一行中有3个1,分别是第3、5、6列。
由于这3列已经包含了1,故,把这三列往下标示,图中的蓝色部分。蓝色部分包含3个1,分别在2行中,把这2行用紫色标示出来
根据定义,同一列的1只能有1个,故紫色的两行,和红色的一行的1相冲突。
那么在接下来的求解中,红色的部分、蓝色的部分、紫色的部分都不能用了,把这些部分都删除,得到一个新的矩阵
行分别对应矩阵1中的第2、4、5行
列分别对应矩阵1中的第1、2、4、7列
于是问题就转换为一个规模小点的精确覆盖问题
在新的矩阵中再选择第1行,如下图所示
还是按照之前的步骤,进行标示。红色、蓝色和紫色的部分又全都删除,导致新的空矩阵产生,而红色的一行中有0(有0就说明这一列没有1覆盖)。说明,第1行选择是错误的
那么回到之前,选择第2行,如下图所示
按照之前的步骤,进行标示。把红色、蓝色、紫色部分删除后,得到新的矩阵
行对应矩阵2中的第3行,矩阵1中的第5行
列对应矩阵2中的第2、4列,矩阵1中的第2、7列
由于剩下的矩阵只有1行,且都是1,选择这一行,问题就解决
于是该问题的解就是矩阵1中第1行、矩阵2中的第2行、矩阵3中的第1行。也就是矩阵1中的第1、4、5行()
(例子引用自http://www.cnblogs.com/grenet/p/3145800.html)