Sudoku POJ - 3074

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("\n");
155     }
156     return 0;
157 }

猜你喜欢

转载自www.cnblogs.com/mzchuan/p/11434887.html