舞蹈链算法
舞蹈链算法用于求解精确覆盖问题 。
精确覆盖问题可以理解为如下问题:
给定一个01矩阵,寻找一个行的集合,使得集合中的列恰好满足每一列包含一个1。
例:矩阵
一个简单的算法思路是,第一步首先选择一行(假定为第一行)
第一行中第四列包含了一个1,而该列同样包含1的是第3行和第五行。是与上次选择相冲突的行列。
在矩阵中去除上述行列(第一行、第三行、第五行、第四列),获得如下矩阵。
所以求解思路可以概括为
1.选择一行
2.去除冲突的行列获得新矩阵
3.若矩阵为空,则判断去除之前的一行是否有全1,若存在则获得结果。若不存在则进行回溯,跳转到步骤1。若无矩阵可回溯,则输出不存在
在进行回溯时,可能会面临大量矩阵缓存的问题。舞蹈链算法就有效解决了这一问题。
舞蹈链算法使用的数据结构是双向十字交叉循环链表。
每一个元素都有6个属性,Left指向左边元素,Right指向右边元素,Up指向上面元素、Down指向下面元素、Col指示所在列,Row指示所在行。
这样做的好处是,我们在做回溯的时候,不再需要大量缓存矩阵。
删除元素时,假定同一行中连续三个元素为a、b、c,需要移除b时,只需要令a.Right = b.Right,c.Left = b.Left。
恢复元素时,只需找到b.Left指向元素a,令a.Right = b,找到b.Right指向元素c,令c.Left = b。
根据这一思路,可以将上述提及的矩阵转化为如下的链表
建立如图所示的链表结构,标识矩阵中为1的元素。
按照上面提及的算法思路,对链表进行操作。改变指针的指向即可达到删减元素的目的。
求解数独问题
数独问题有四个约束条件,即
1.所有格子必须填满数字
2.每一行都是1~9的一个排列
3.每一列都是1~9的一个排列
4.每一区域都是1~9的一个排列
可以将其转化为一个精确覆盖问题。
将矩阵的列定义为满足上述的一个约束条件,及第1~81列表示为约束条件:每个格子都有数字
第82~第162列分别定义为第一行存在数字1,第一行存在数字2…第一行存在数字9…第9行存在数字9
第163~第243列分别定义为第一列存在数字1,第一列存在数字2…第一列存在数字9…第9列存在数字9
第244~第234列分别定义为第一区域存在数字1,第一区域存在数字2…第一区域存在数字9…第9区域存在数字9
将可能的填入情况作为行,将约束条件作为列。
对于数独中已有的元素,可将其进行转化,如(1,3)中填入4,则代表满足了约束条件
1. (1,3)有数字
2. 第1行有4
3. 第3列有4
4. 第1区域有4
则第(1-1)×9+3=3列,
第(1-1)×9+4 + 81=85列,
第(3-1)×9+4 + 162=184列,
第(1-1)×9+4 + 243=247列为1,其余列为0。
将这样的一行插入到矩阵中。
对于数独中未知的元素,则分9种情况讨论,假定需要确定(1,3)位置的值。
假定填入1,则第3,82,181,244列为1,其余为0。
假定填入2,则第3,83,182,245列为1,其余为0。
…
将这样的9行插入到矩阵中。
将所有已知和未知元素都按照这样的方法插入到矩阵中以后。问题就转化为了一个精确覆盖问题。可以采用舞蹈链算法进行求解。