• Sudoku POJ


    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 }
  • 相关阅读:
    ES 入门记录之 match和term查询的区别
    ElasticSearch 学习记录之Text keyword 两种基本类型区别
    ES 记录之如何创建一个索引映射,以及一些设置
    娱乐至死 读书笔记
    ES 入门之一 安装ElasticSearcha
    王二的经济学故事 读书笔记
    redis特性 存储 API 集群 等
    Linux 卸载 openjdk
    Linux 下面解压.tar.gz 和.gz文件解压的方式
    本地连接 vmware服务器
  • 原文地址:https://www.cnblogs.com/mzchuan/p/11434887.html
Copyright © 2020-2023  润新知