DLX算法求解数独

首先,DLX算法并不是为了解数独而提出的,它是为了解决精确覆盖问题而被提出的。
精确覆盖问题:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1。
数独这个问题恰好拟合了DLX算法的模型,所以可以用该方法求解。
DLX算法的具体分析请看跳跃的舞者,舞蹈链(Dancing Links)算法——求解精确覆盖问题。该帖讲述的十分详细,就不在这里赘述算法的本身了。DLX算法实质上是一种解决问题的模型,它是一种通用的搜索方法,只需要进行适当的转化与建模,就能将一个复杂问题转化为精确覆盖问题。再配合上双向十字链表的优化,大大降低递归成本。
在上面的精确覆盖问题中,我们可以分析得到两个要点:1.要求每列都被覆盖;2.恰好包含一个1,也就是说要恰好每列都被一个1覆盖,也就是“有且只有一个”。所以在分析问题的时候,我们可以将行可以看做是我们可以选择的解空间,而列则是该解空间所要满足的要求。
比如说数独,对于数独的DLX构造,先来分析行。对于数独的每个格子,都有9种填法,所以最后有9*81=729种,DLX的行就是729行。而对于列,首先,对于数独的81个格子,每个格子都只能填一个数,所以需要81列来保证每个格子中有且只有一个数;其次,对于数独的每一行,都需要满足有且恰好只有一个1,有且恰好只有一个2,……,有且恰好只有一个9,所以每一行都需要9列,9行就需要81列;同理,列与九宫格与行所要满足的要求是一样的。所以最后一共需要81*4=324列来满足数独的题目要求。在上述所有列的分析中,需要满足的条件都是“有且只有一个”,这与前面的覆盖问题恰好吻合。
对于DLX数组的列坐标分配:
0~80列——格
81~161列——行
162~242列——列
243~323列——九宫格
所以设row为行坐标,col为列坐标,num=9 * row + col,则在数独中
1.对于每个未知的点来说:
for (int i = 0; i < 9; i++)
{
Matrix[num * 9 + i][num] = 1;//每格唯一
Matrix[num * 9 + i][81 + row * 9 + i] = 1;//每行每个数唯一
Matrix[num * 9 + i][162 + col * 9 + i] = 1;//每行每个数唯一
Matrix[num * 9 + i][243 + (row / 3 * 3 + col / 3) * 9 + i] = 1;
//每九宫格每个数唯一
}
对于一个未知的点,它能填9个数,所以将所有的需求都添加进去;
2.对于一个已知的点来说:
{
i = sudoku[row][col] - '1';//得到该数
Matrix[num * 9 + i][num] = 1;//每格唯一
Matrix[num * 9 + i][81 + row * 9 + i] = 1;//每行每个数唯一
Matrix[num * 9 + i][162 + col * 9 + i] = 1;//每行每个数唯一
Matrix[num * 9 + i][243 + (row / 3 * 3 + col / 3) * 9 + i] = 1;
//每九宫格每个数唯一
}

对于一个已知的点,它的num列是唯一的,所以为了满足要求,在搜索过程中,它是一定会被选中的,这样就保证了该点的选择的唯一性。

在后续过程中,则需要建立一个双向十字链表,然后进行DFS。过程很繁琐,就不在这里赘述了。

上述就是对数独问题用DLX算法进行求解的建模了。可以发现,如果一个问题可以用DLX求解,那么只要通过适当的建模,就可以把任何复杂的问题转化为一个已知解法的搜索问题。而在建模的时候,抓住“有且只有一个”这一重要条件,就可以轻松理解问题,并加以转化。

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页