In the game of Sudoku, you are given a large 9 × 9 grid divided into smaller 3 × 3 subgrids. For example,
. | 2 | 7 | 3 | 8 | . | . | 1 | . |
. | 1 | . | . | . | 6 | 7 | 3 | 5 |
. | . | . | . | . | . | . | 2 | 9 |
3 | . | 5 | 6 | 9 | 2 | . | 8 | . |
. | . | . | . | . | . | . | . | . |
. | 6 | . | 1 | 7 | 4 | 5 | . | 3 |
6 | 4 | . | . | . | . | . | . | . |
9 | 5 | 1 | 8 | . | . | . | 7 | . |
. | 8 | . | . | 6 | 5 | 3 | 4 | . |
Given some of the numbers in the grid, your goal is to determine the remaining numbers such that the numbers 1 through 9 appear exactly once in (1) each of nine 3 × 3 subgrids, (2) each of the nine rows, and (3) each of the nine columns.
Input
The input test file will contain multiple cases. Each test case consists of a single line containing 81 characters, which represent the 81 squares of the Sudoku grid, given one row at a time. Each character is either a digit (from 1 to 9) or a period (used to indicate an unfilled square). You may assume that each puzzle in the input will have exactly one solution. The end-of-file is denoted by a single line containing the word “end”.
Output
For each test case, print a line representing the completed Sudoku puzzle.
Sample Input
.2738..1..1...6735.......293.5692.8...........6.1745.364.......9518...7..8..6534. ......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3. end
Sample Output
527389416819426735436751829375692184194538267268174593643217958951843672782965341 416837529982465371735129468571298643293746185864351297647913852359682714128574936
题意:给出一个9*9的数独,求解;
思路:优先使用的是dlx算法,不过有一点改变。首先看如何转换成dlx算法:
那利用舞蹈链(Dancing Links)算法求解数独问题,实际上就是下面一个流程
1、把数独问题转换为精确覆盖问题
2、设计出数据矩阵
3、用舞蹈链(Dancing Links)算法求解该精确覆盖问题
4、把该精确覆盖问题的解转换为数独的解
首先看看数独问题(9*9的方格)的规则
1、每个格子只能填一个数字
2、每行每个数字只能填一遍
3、每列每个数字只能填一遍
4、每宫每个数字只能填一遍
那现在就是利用这个规则把数独问题转换为精确覆盖问题
可是,直观上面的规则,发现比较难以转换为精确覆盖问题。因此,把上面的表述换个说法
1、每个格子只能填一个数字
2、每行1-9的这9个数字都得填一遍(也就意味着每个数字只能填一遍)
3、每列1-9的这9个数字都得填一遍
4、每宫1-9的这9个数字都得填一遍
这样理解的话,数独问题转换为精确覆盖问题就相对简单多了。关键就是如何构造精确覆盖问题中的矩阵
我们把矩阵的每个列都定义成一个约束条件。
第1列定义成:(1,1)填了一个数字
第2列定义成:(1,2)填了一个数字
……
第9列定义成:(1,9)填了一个数字
第10列定义成:(2,1)填了一个数字
……
第18列定义成:(2,9)填了一个数字
……
第81列定义成:(9,9)填了一个数字
至此,用第1-81列完成了约束条件1:每个格子只能填一个数字
第N列(1≤N≤81)定义成:(X,Y)填了一个数字。
N、X、Y之间的关系是:X=INT((N-1)/9)+1;Y=((N-1) Mod 9)+1;N=(X-1)×9+Y
第82列定义成:在第1行填了数字1
第83列定义成:在第1行填了数字2
……
第90列定义成:在第1行填了数字9
第91列定义成:在第2行填了数字1
……
第99列定义成:在第2行填了数字9
……
第162列定义成:在第9行填了数字9
至此,用第82-162列(共81列)完成了约束条件2:每行1-9的这9个数字都得填一遍
第N列(82≤N≤162)定义成:在第X行填了数字Y。
N、X、Y之间的关系是:X=INT((N-81-1)/9)+1;Y=((N-81-1) Mod 9)+1;N=(X-1)×9+Y+81
第163列定义成:在第1列填了数字1
第164列定义成:在第1列填了数字2
……
第171列定义成:在第1列填了数字9
第172列定义成:在第2列填了数字1
……
第180列定义成:在第2列填了数字9
……
第243列定义成:在第9列填了数字9
至此,用第163-243列(共81列)完成了约束条件3:每列1-9的这9个数字都得填一遍
第N列(163≤N≤243)定义成:在第X列填了数字Y。
N、X、Y之间的关系是:X=INT((N-162-1)/9)+1;Y=((N-162-1) Mod 9)+1;N=(X-1)×9+Y+162
第244列定义成:在第1宫填了数字1
第245列定义成:在第1宫填了数字2
……
第252列定义成:在第1宫填了数字9
第253列定义成:在第2宫填了数字1
……
第261列定义成:在第2宫填了数字9
……
第324列定义成:在第9宫填了数字9
至此,用第244-324列(共81列)完成了约束条件4:每宫1-9的这9个数字都得填一遍
第N列(244≤N≤324)定义成:在第X宫填了数字Y。
N、X、Y之间的关系是:X=INT((N-243-1)/9)+1;Y=((N-243-1) Mod 9)+1;N=(X-1)×9+Y+243
至此,用了324列完成了数独的四个约束条件,矩阵的列定义完成
那接下来,就是把数独转换为矩阵
数独问题中,每个格子分两种情况。有数字的格子、没数字的格子。
有数字的格子
以例子来说明,在(4,2)中填的是7
把(4,2)中填的是7,解释成4个约束条件
1、在(4,2)中填了一个数字。
2、在第4行填了数字7
3、在第2列填了数字7
4、在第4宫填了数字7(坐标(X,Y)到宫N的公式为:N=INT((X-1)/3)×3+INT((Y-1)/3)+1)
那么这4个条件,分别转换成矩阵对应的列为
1、在(4,2)中填了一个数字。对应的列N=(4-1)×9+2=29
2、在第4行填了数字7。对应的列N=(4-1)×9+7+81=115
3、在第2列填了数字7。对应的列N=(2-1)×9+7+162=178
4、在第4宫填了数字7。对应的列N=(4-1)×9+7+243=277
于是,(4,2)中填的是7,转成矩阵的一行就是,第29、115、178、277列是1,其余列是0。把这1行插入到矩阵中去。
没数字的格子
还是举例说明,在(5,8)中没有数字
把(5,8)中没有数字转换成
(5,8)中填的是1,转成矩阵的一行就是,第44、118、226、289列是1,其余列是0。
(5,8)中填的是2,转成矩阵的一行就是,第44、119、227、290列是1,其余列是0。
(5,8)中填的是3,转成矩阵的一行就是,第44、120、228、291列是1,其余列是0。
(5,8)中填的是4,转成矩阵的一行就是,第44、121、229、292列是1,其余列是0。
(5,8)中填的是5,转成矩阵的一行就是,第44、122、230、293列是1,其余列是0。
(5,8)中填的是6,转成矩阵的一行就是,第44、123、231、294列是1,其余列是0。
(5,8)中填的是7,转成矩阵的一行就是,第44、124、232、295列是1,其余列是0。
(5,8)中填的是8,转成矩阵的一行就是,第44、125、233、296列是1,其余列是0。
(5,8)中填的是9,转成矩阵的一行就是,第44、126、234、297列是1,其余列是0。
把这9行插入到矩阵中。由于这9行的第44列都是1(不会有其他行的44列会是1),也就是说这9行中必只有1行(有且只有1行)选中(精确覆盖问题的定义,每列只能有1个1),是最后解的一部分。这就保证了最后解在(5,8)中只有1个数字。
这样,从数独的格子依次转换成行(1行或者9行)插入到矩阵中。完成了数独问题到精确覆盖问题的转换。同时在转换的过程中要把这个数的位置同时记住,用一个数组来保存。
然后有一个优化:在dancing(dep)函数调用的时候是直接调用_Head.Right来获得未求解列。由于精确覆盖问题是要求每个列都要覆盖到,因此,在算法中调用未求解列的先后顺序那就不是最重要了。假如,现在有两个未求解列C1和C2,C1列有8个元素,C2列有4个元素。最坏的情况,从C1列求解,需要调用8次Dance(K+1),而从C2列求解,需要调用4次Dance(K+1)。感觉上从C2列求解比从C1列求解效率要高些。因此,在Dance(K)函数调用的时候,先找寻列元素最少的未求解列,再依次求解,可能效率会高点。
代码:
1 #include <cstdio> 2 #include <fstream> 3 #include <algorithm> 4 #include <cmath> 5 #include <deque> 6 #include <vector> 7 #include <queue> 8 #include <string> 9 #include <cstring> 10 #include <map> 11 #include <stack> 12 #include <set> 13 #include <sstream> 14 #include <iostream> 15 #define mod 1000000007 16 #define eps 1e-6 17 #define ll long long 18 #define INF 0x3f3f3f3f 19 using namespace std; 20 21 const int ms=81*10; 22 const int maxn=ms*4; 23 int ans[maxn]; 24 struct DLX 25 { 26 int n,id; 27 int L[maxn],R[maxn],U[maxn],D[maxn]; 28 int C[maxn],S[maxn],loc[maxn][3];//C代表列,S代表每列有的数字数量,loc代表这个数在数独中的位置和数值 29 int H[ms]; 30 void init(int nn=0) 31 { 32 n=nn; 33 for(int i=0;i<=n;i++) U[i]=D[i]=i,L[i]=i-1,R[i]=i+1; 34 L[0]=n; R[n]=0; 35 id=n; 36 memset(S,0,sizeof(S)); 37 memset(H,-1,sizeof(H)); 38 } 39 void Link(int x,int y,int px,int py,int k) 40 { 41 ++id; 42 D[id]=y; U[id]=U[y]; 43 D[U[y]]=id; U[y]=id; 44 loc[id][0]=px,loc[id][1]=py,loc[id][2]=k;//存放数的位置和数 45 C[id]=y; 46 S[y]++;//此列1的数量加一 47 if(H[x]==-1) H[x]=L[id]=R[id]=id; 48 else 49 { 50 int a=H[x]; 51 int b=R[a]; 52 L[id]=a; R[a]=id; 53 R[id]=b; L[b]=id; 54 H[x]=id; 55 } 56 } 57 void Remove(int c) 58 { 59 L[R[c]]=L[c]; 60 R[L[c]]=R[c]; 61 for(int i=D[c];i!=c;i=D[i]) 62 for(int j=R[i];j!=i;j=R[j]) 63 { 64 U[D[j]]=U[j]; 65 D[U[j]]=D[j]; 66 S[C[j]]--; 67 } 68 } 69 void Resume(int c) 70 { 71 for(int i=U[c];i!=c;i=U[i]) 72 for(int j=R[i];j!=i;j=R[j]) 73 { 74 S[C[j]]++; 75 U[D[j]]=j; 76 D[U[j]]=j; 77 } 78 L[R[c]]=c; 79 R[L[c]]=c; 80 } 81 bool dfs(int step) 82 { 83 if(step==81) return true; 84 if(R[0]==0) return false; 85 int Min=INF,c=-1; 86 for(int i=R[0];i;i=R[i])//优先循环1的数量少的一列 87 if(Min>S[i]){ Min=S[i]; c=i; } 88 Remove(c); 89 for(int i=D[c];i!=c;i=D[i]) 90 { 91 ans[step]=i; 92 for(int j=R[i];j!=i;j=R[j]) Remove(C[j]); 93 if(dfs(step+1)) return true; 94 for(int j=L[i];j!=i;j=L[j]) Resume(C[j]); 95 } 96 Resume(c); 97 return false; 98 } 99 }dlx; 100 int main() 101 { 102 char S[90]; 103 while(scanf("%s",S)!=EOF) 104 { 105 if(S[0]=='e') break; 106 dlx.init(81*4); 107 int k=0,r=0;//r代表行 108 for(int x=0;x<9;x++) 109 for(int y=0;y<9;y++) 110 { 111 char ch=S[k++]; 112 int a,b,c,d;//a表示约束一,b表示约束二,c表示约束三,d表示约束四 113 if(ch=='.') 114 { 115 for(int i=1;i<=9;i++) 116 { 117 a=x*9+y+1; 118 b=x*9+i+81; 119 c=y*9+i+81+81; 120 int s=(x/3)*3+y/3; 121 d=s*9+i+81+81+81; 122 ++r; 123 dlx.Link(r,a,x,y,i); 124 dlx.Link(r,b,x,y,i); 125 dlx.Link(r,c,x,y,i); 126 dlx.Link(r,d,x,y,i); 127 } 128 } 129 else 130 { 131 int i=ch-'0'; 132 a=x*9+y+1; 133 b=x*9+i+81; 134 c=y*9+i+81+81; 135 int s=(x/3)*3+y/3; 136 d=s*9+i+81+81+81; 137 ++r; 138 dlx.Link(r,a,x,y,i); 139 dlx.Link(r,b,x,y,i); 140 dlx.Link(r,c,x,y,i); 141 dlx.Link(r,d,x,y,i); 142 } 143 } 144 dlx.dfs(0); 145 int res[10][10]; 146 for(int i=0;i<81;i++)//将答案存放到一个数独数组中 147 { 148 int a=ans[i]; 149 int x=dlx.loc[a][0],y=dlx.loc[a][1],k=dlx.loc[a][2]; 150 res[x][y]=k; 151 } 152 for(int i=0;i<9;i++) 153 for(int j=0;j<9;j++) printf("%d",res[i][j]); 154 printf(" "); 155 } 156 return 0; 157 }