Dancing Link是解决精确覆盖问题的最有效方法之一。
精确覆盖问题:给定一个由0和1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1?
例如,下面这个矩阵
就包含了这样一个集合(第1,4,5行)。我们把列想象成全集的一些元素,而行看作全集的一些子集;或者我们可以把行想象成全集的一些元素,而把列看作全集的一些子集;那么这个问题就是要求寻找一批元素,它们与每个子集恰好有一个交点。
算法X:对于接下来的非确定性算法,由于我们没有想到更好的名字,我们将称之为X算法,它能够找到由特定的01矩阵A定义的精确覆盖问题的所有解。X算法是实现试验——错误这一显而易见的方法的一段简单的语句(确实,一般来说,我想不到别的合理的方法来完成这个工作)。
如果A是空的,问题解决;成功终止。
否则,选择一个列c(确定的)。
选择一个行r,满足 A[r, c]=1 (不确定的)。
把r包含进部分解。
对于所有满足 A[r,j]=1 的j,
从矩阵A中删除第j列;
对于所有满足 A[i,j]=1 的i,
从矩阵A中删除第i行。
在不断减少的矩阵A上递归地重复上述算法。
舞蹈步骤:
一个实现X算法的好方法就是将矩阵A中的每个1用一个有5个域L[x]、R[x]、U[x]、D[x]、C[x]的数据对象(data object)x来表示。矩阵的每行都是一个经由域L和R(“左”和“右”)双向连接的环状链表;矩阵的每列是一个经由域U和D(“上”和“下”)双向连接的环状链表。每个列链表还包含一个特殊的数据对象,称作它的表头(list header)。
这些表头是一个称作列对象(column object)的大型对象的一部分。每个列对象y包含一个普通数据对象的5个域L[y]、R[y]、U[y]、D[y]和C[y],外加两个域S[y](大小)和N[y](名字);这里“大小”是一个列中1的个数,而“名字”则是用来标识输出答案的符号。每个数据对象的C域指向相应列头的列对象。
表头的L和R连接着所有需要被覆盖的列。这个环状链表也包含一个特殊的列对象称作“根”,h,它相当于所有活动表头的主人。而且它不需要U[h]、D[h]、C[h]、S[h]和N[h]这几个域。
举个例子,(3)中的0-1矩阵将用这些数据对象来表示,就像图2展示的那样,我们给这些列命名为A、B、C、D、E、F和G(这个图表在上下左右处“环绕扭曲”。C的连线没有画出,因为他们会把图形弄乱;每个C域指向每列最顶端的元素)。
我们寻找所有精确覆盖的不确定性算法现在可以定型为下面这个明析、确定的形式,即一个递归过程search(k),它一开始被调用时k=0:
如果 R[h]=h ,打印当前的解(见下)并且