DLX又称作精确覆盖问题,而今天要说的就是把解数独这个问题转化为一个精确覆盖问题,然后来解决它。
其实这是一个很好的解决问题的思路,很多问题都可以转化为一个精确覆盖问题,然后用接下来这个算法来解决它。
关于DLX的思想,网上有不少的解释,这里我就不再赘述,我会在我的另一篇博客里讲解原理。

首先是整个代码的结构。
总共分为四个类,分别是:

  1. 主类(StartPoint)
  2. 转换器类(Transfer)
  3. 节点类(Node)
  4. 求解器类(Solver)

这就是我们的代码的主要结构,下面我们将展示代码的细节。

主类(StartPoint)

这是主类,也是整个程序的入口,负责从文件读取数据,调用各种其他类,然后输出结果到文件中,具体如下:

package dLX;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Vector;

public class StartPoint {

public static void main(String[] args) {
// TODO Auto-generated method stub
File input=new File("D:\\problem.txt");
File output = new File("D:\\solution.txt");
try {
output.createNewFile();
FileReader in=new FileReader(input);
FileWriter out=new FileWriter(output);
char problem[]=new char[(int)input.length()];
in.read(problem);
if(problem.length<81){
out.write("输入不合法");
in.close();
out.close();
return;
}
Transfer tsf= new Transfer();
Vector<Vector<Integer>> matrix = tsf.sudoku2matrix(problem);// 矩阵转换
Solver sudoku=new Solver(matrix,matrix.size(),324);//初始化解题类

if (!sudoku.Search(0)){//调用解题函数
out.write("无解");
in.close();
out.close();
return;
}
Vector<Integer> solution = tsf.matrix2sudoku(matrix, sudoku.getResult());
for (int ix = 0; ix < 81; ++ix){
out.write(solution.get(ix)+"");
out.write((ix+1)%9!=0?"" : "\n");
}
in.close();
out.close();


} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}


}

节点类(Node)

因为DLX很关键的一个数据结构是链表,所以就需要节点,这里我们就写了一个节点类:

package dLX;

public class Node {
Node up, down, left, right, colRoot, rowRoot;
int Num;//行对象特有,记录行数
int Size;//列对象特有,记录该列元素数

public Node(){
Size=0;
Num=-1;
}

public Node(int i){
Size=0;
Num=i;
}
}

转换器类

因为我们人看到的数独矩阵并不是我们解题需要的01矩阵,所有我们需要一个转换的类,这个类负责把输入的数独矩阵转换成01矩阵便于解题,当解题结束时,再负责把结果转换成便于人类阅读的数独矩阵,这就是这个类的作用:

package dLX;

import java.util.Vector;

public class Transfer {
Vector<Vector<Integer>> sudoku2matrix(char[] problem){
Vector<Vector<Integer>> matrix=new Vector<Vector<Integer>>();
for (int ix = 0; ix < 81; ++ix){
int val = problem[ix] - '0';
Vector<Integer> current_row=new Vector<Integer>();
for(int i=0;i<324;i++){
current_row.add(new Integer(0));
}
if (val != 0){
current_row.set(ix, new Integer(1));
current_row.set(81 + ix/9*9 + val -1, new Integer(1));
current_row.set(162 + ix%9*9 +val -1, new Integer(1));
current_row.set(243 + (ix/9/3*3+ix%9/3)*9 +val -1, new Integer(1));
matrix.add(current_row);
continue;
}
for (int jx = 0; jx < 9; ++jx){
Vector<Integer> current_row2=new Vector<Integer>();
for(int i=0;i<324;i++){
current_row2.add(new Integer(0));
}
current_row2.set(ix, new Integer(1));
current_row2.set(81 + ix/9*9 + jx, new Integer(1));
current_row2.set(162 + ix%9*9 +jx , new Integer(1));
current_row2.set(243 + (ix/9/3*3+ix%9/3)*9 +jx , new Integer(1));
matrix.add(current_row2);
}
}
return matrix;
}


Vector<Integer> matrix2sudoku(Vector<Vector<Integer>> matrix, Vector<Integer> result){
Vector<Integer> solution=new Vector<Integer>();
for(int i=0;i<81;i++){
solution.add(new Integer(0));
}
for (int ix = 0; ix < 81; ++ix){
Vector<Integer> current = matrix.get(result.get(ix)-1);
int pos = 0, val = 0;
for (int jx = 0; jx < 81; ++jx){
if (current.get(jx) == 1){
break;
}
++pos;
}
for (int kx = 81; kx < 162; ++kx){
if (current.get(kx) == 1)
break;
++val;
}
solution.set(pos, val%9 + 1);
}
return solution;
}
}

求解器类

这个类就是解决问题的核心了,它负责核心的解决问题,接收转换类转换得到的01矩阵来解决问题,提供结果,具体代码如下:

package dLX;

import java.util.Vector;

public class Solver {
Node Head;
Vector<Integer> result;
int _row, _col, _updates;
public static final int INT_MAX=2147483647;

public Solver(Vector<Vector<Integer>> matrix, int m, int n){
result=new Vector<Integer>();
Head = new Node();
Head.up = Head;
Head.down = Head;
Head.right = Head;
Head.left = Head;
_row=m;
_col=n;
_updates=0;
init();
link(matrix);
}

void init(){
Node newNode;
//表头位置向后插入,构造列对象
for (int ix = 0; ix < _col; ++ix){
newNode = new Node();
newNode.up = newNode;
newNode.down = newNode;
newNode.right = Head.right;
newNode.left = Head;
newNode.right.left = newNode;
Head.right = newNode;
}
//表头位置向下插入,构造行对象
for (int ix = 0; ix < _row; ++ix){
newNode = new Node(_row-ix);//注意序号是_row-ix
newNode.down = Head.down;
newNode.up = Head;
newNode.down.up = newNode;
Head.down = newNode;
}
}

void link(Vector<Vector<Integer>> matrix){
Node current_row, current_col, newNode, current;//当前行对象,当前列对象,新节点,当前节点
current_row = Head;
for (int row = 0; row < _row; ++row){
current_row = current_row.down;
current_col = Head;
for (int col = 0; col < _col; ++col){
current_col = current_col.right;
if (matrix.get(row).get(col) == 0)//矩阵上为0的位置不设置节点
continue;
newNode = new Node();
newNode.colRoot = current_col;
newNode.rowRoot = current_row;//设置当前节点对应的行列对象

newNode.down = current_col;
newNode.up = current_col.up;
newNode.up.down = newNode;
current_col.up = newNode;//链接当前节点到列双向链尾端

if (current_row.Size == 0){//行双向链不应该把行对象包含进来
current_row.right = newNode;
newNode.left = newNode;
newNode.right = newNode;
current_row.Size++;
}
current = current_row.right;//设置当前节点(即行对象右的节点)
newNode.left = current.left;
newNode.right = current;
newNode.left.right = newNode;
current.left = newNode;//链接当前节点到行双向链尾端

current_col.Size++;
}
}
}

void cover(Node cRoot){//覆盖列
++_updates;
cRoot.left.right = cRoot.right;
cRoot.right.left = cRoot.left;//删除该列对象
Node i, j;
i = cRoot.down;
while (i != cRoot){
j = i.right;
while (j != i){
j.down.up = j.up;
j.up.down = j.down;
j.colRoot.Size--;
j = j.right;
}
i = i.down;
}
}

void recover(Node cRoot){
Node i, j;
i = cRoot.up;
while (i != cRoot){
j = i.left;
while (j != i){
j.colRoot.Size++;
j.down.up = j;
j.up.down = j;
j = j.left;
}
i = i.up;
}
cRoot.right.left = cRoot;
cRoot.left.right = cRoot;
}

boolean Search(int k){

if (Head.right == Head)//列空,则成功找到一组行的集合
return true;

Node cRoot, c;
cRoot=new Node();
int minSize = INT_MAX;
for(c = Head.right; c != Head; c = c.right){//选择情况最少的列进行尝试
if (c.Size < minSize){
minSize = c.Size;
cRoot = c;
if (minSize == 1){
break;
}

if (minSize == 0){//有一列为空,失败
return false;
}
}
}
cover(cRoot);//覆盖这一列

Node current_row, current;
for (current_row = cRoot.down; current_row != cRoot; current_row = current_row.down){
result.add(current_row.rowRoot.Num);//将该行加入result中
for (current = current_row.right; current != current_row; current = current.right){
cover(current.colRoot);//delete other c
}
if (Search(k+1)){
return true;
}
for (current = current_row.left; current != current_row; current = current.left)
recover(current.colRoot);//还原
result.remove(result.size()-1);//发现该行不符合要求,还原result
}
recover(cRoot);
return false;
}


Vector<Integer> getResult(){
return result;
}

int getUpdates(){
return _updates;
}





}