DLX又稱作精確覆蓋問題,而今天要說的就是把解數獨這個問題轉化爲一個精確覆蓋問題,然後來解決它。
其實這是一個很好的解決問題的思路,很多問題都可以轉化爲一個精確覆蓋問題,然後用接下來這個算法來解決它。
關於DLX的思想,網上有不少的解釋,這裏我就不再贅述,我會在我的另一篇博客裏講解原理。
首先是整個代碼的結構。
總共分爲四個類,分別是:
- 主類(StartPoint)
- 轉換器類(Transfer)
- 節點類(Node)
- 求解器類(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;
}
}