首页 > 技术文章 > [ C语言版 ] 数独计算器 [ 搜索剪枝法 ]

Gster 2018-06-30 18:51 原文

【原创】转载请注明出处。 【浙江大学 程序设计专题】

使用方法:按提示输入方式为9*9的矩阵,0表示未知数。

为解决这一问题,我们也尝试了两种方法,准确的说,是第一种方法太慢了,我们对它进行了优化。

方法一:
用最朴素的方法逐一枚举每一个格子,在某一个格子不能填入任何数字时回溯。
这个方法写法相对简单,但对于一些难以求解的情况,程序会非常慢,最慢的甚至无法在3秒内得出答案。如果每一个情况需要三秒钟来求解,那么要批量求解数独可能需要等好几分钟。
尽管我们将编译器的优化选项开到最高,一些情况仍需要1秒左右。

方法二:
模拟手算,当给定一道题目时,首先确定每个格子可以填那些数字,每次优先选择可选数字最少的格子。
此方法对于大多数数据能在0.1秒内求解。

 

方法一样例解释:绿色数字表示有多重可能的格子,红色数字表示唯一可能的格子,红色填充的格子表示无法填入数字。

方法二样例:

方法一代码:

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <stdlib.h>
  4 #include <time.h>
  5 #include "prnt_sudoku.h"
  6 
  7 /*Sudoku save in array Map, index counts from 0.*/
  8 int    Map[9][9],cnt;
  9 /*R<->Row  B<->Block  C<->Column*/
 10 int    R[11][11],B[11][11],C[11][11];
 11 /*characters on the left and right of output-numbers.*/
 12 /*this will make user know which numbers are given at first.*/
 13 char    C1[9][9],C2[9][9];
 14 
 15 int    Dfs(const int x,const int y)
 16 {
 17     if(x==9)
 18     {
 19         printf("\n\n\tSolution #%d:  \n\n",++cnt);
 20         Print(Map,C1,C2);
 21         /*when there are too many solutions return 1*/
 22         /*and the whole Search algorithm will stop*/
 23         if(cnt==5000) return 1;
 24         return 0;
 25     }
 26 
 27     if(Map[x][y]) { return Dfs(x+(y+1)/9,(y+1)%9); }
 28     /*if (x,y) is known, then fill the next one.*/
 29 
 30     int i;
 31     for(i=1;i<=9;++i)
 32     {
 33         /*Check if setting i in blank(x,y) is proper.*/
 34         if(R[x][i] || C[y][i] || B[x/3*3+y/3][i])continue;
 35 
 36         /*update arrays*/
 37         Map[x][y]=i;
 38         R[x][i]=1; C[y][i]=1; B[x/3*3+y/3][i]=1;
 39 
 40         if(Dfs(x+(y+1)/9,(y+1)%9)) return 1;
 41         /*this expression will fill blanks from the left-up one to */
 42         /*  the right-down one automaticly.*/
 43 
 44         /*undo*/
 45         R[x][i]=0; C[y][i]=0; B[x/3*3+y/3][i]=0;
 46         Map[x][y]=0;
 47     }
 48     return 0;
 49 }
 50 
 51 
 52 int main()
 53 {
 54     int op,T=0;
 55     system("cls");
 56     printf("Choose input/output way(1.Keyboard/2.File):  ");
 57     while(1)/*until receiving an expected input.*/
 58     {
 59         scanf("%d",&op);
 60         if(op==1)break;
 61         if(op==2)/*file input*/
 62         {
 63             char File_Name[110];
 64             printf("Please input File_Name:  ");
 65             scanf("%s",File_Name);
 66             printf("\n\tResult Will Save to Result.txt\n\n");
 67             printf("\tCalculating....\n\n");
 68             freopen(File_Name,"r",stdin);
 69             freopen("Result.txt","w",stdout);/*answer file is "Result.txt" */
 70             break;
 71         }
 72     }
 73 
 74     while(1)/*Support multi-set test, read to EOF.*/
 75     {
 76         /*Initialize*/
 77         memset(R,0,sizeof(R));
 78         memset(C,0,sizeof(C));
 79         memset(B,0,sizeof(B));
 80         memset(Map,0,sizeof(Map));
 81         cnt=0;
 82         if(op==1)printf("Please Input 9*9 matrix(0 for space):\n");
 83         int i,j,data;
 84         /*Read until EOF*/
 85         for(i=0;i<9;++i) for(j=0;j<9;++j)
 86         {
 87             if(!(~scanf("%1d",&data)))
 88             {
 89                 if(op==1)system("pause");
 90                 fclose(stdin);
 91                 return 0;
 92             }
 93             Map[i][j]=data;
 94             R[i][data]++;
 95             C[j][data]++;
 96             B[i/3*3+j/3][data]++;
 97         }
 98 
 99         printf("Test Case #%d: ",++T);
100         int f=0;/*Check if the sudoku is valid.*/
101         for(i=0;i<9;++i) for(j=1;j<=9;++j)
102             if(R[i][j]>1|| C[i][j]>1 || B[i][j]>1) f=1;
103         if(f)/*Invalid input*/
104         {
105             fprintf(stderr,"\n\t\tError in Test Case #%d: ",T);
106             fprintf(stderr,"Invalid Input.\n");
107             printf("\n\n\tFiled: No Solution Found!\n\n");
108             continue;
109         }
110 
111         /*Mark the known grids.*/
112         for(i=0;i<9;++i) for(j=0;j<9;++j)
113                 if(Map[i][j])C1[i][j]='[',C2[i][j]=']';
114                 else C1[i][j]=C2[i][j]=' ';
115 
116         int t1=clock();/*Timer*/
117         if(Dfs(0,0))
118             /*In case of costing too much time and disk storage,*/
119             /*Dfs(int,int) will find at most 5000 kinds of solution.*/
120         {
121             fprintf(stderr,"\n\t\tError in Test Case #%d: ",T);
122             fprintf(stderr,"Too much solution! Calculation has broken.\n");
123         }
124 
125         /*output the time*/
126         if(cnt==0) printf("\n\n\tFiled: No Solution Found!\n\n");
127         else printf("\n\n\t%d Solution Found In %ldms.\n\n",cnt,1000*(clock()-t1)/CLOCKS_PER_SEC);
128     }
129 
130     return 0;
131 }
View Code

方法二代码:

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <stdlib.h>
  4 #include <time.h>
  5 #include "prnt_sudoku.h"
  6 
  7 struct PII { int x,y; };
  8 
  9 /*Sudoku save in array Map, index counts from 0.*/
 10 int    Map[9][9],cnt;
 11 /*F[i][j][k] refers to that, in grid (i,j), number k is used if F[i][j][k]!=0*/
 12 int    F[9][9][10];
 13 /*characters on the left and right of output-numbers.*/
 14 /*this will make user know which numbers are given at first.*/
 15 char    C1[9][9],C2[9][9];
 16 
 17 /*Mark Row x and Column y,and the 3*3 grid that number 'data' has been used.*/
 18 void    Update(const int x,const int y,const int data)
 19 {
 20     int i,px=x/3,py=y/3;
 21     for(i=0;i<9;++i)
 22     {
 23         F[x][i][data]++;/*Row*/
 24         F[i][y][data]++;/*Column*/
 25         F[px*3+i/3][py*3+i%3][data]++;/*Block*/
 26     }
 27 }
 28 
 29 /*Undo 'Update'.*/
 30 void    Undo_update(const int x,const int y,const int data)
 31 {
 32     int i,px=x/3,py=y/3;
 33     for(i=0;i<9;++i)
 34     {
 35         F[x][i][data]--;/*Row*/
 36         F[i][y][data]--;/*Column*/
 37         F[px*3+i/3][py*3+i%3][data]--;/*Block*/
 38     }
 39 }
 40 
 41 /*Returns a pair of int (x,y) referring to the next grid should fill.*/ 
 42 struct PII Get_next()
 43 {
 44     int i,j,k,Min=0x7fffffff;
 45     struct PII pos;
 46     for(i=0;i<9;++i)
 47         for(j=0;j<9;++j)
 48         {
 49             if(Map[i][j])continue;
 50             int cc=0;/*to count how many numbers are not used*/
 51             for(k=1;k<=9;++k)
 52                 if(!F[i][j][k])cc++;
 53             if(cc==0) { pos.x=-1; return pos; }
 54             if(cc<Min)Min=cc,pos.x=i,pos.y=j;
 55             /*Choose the grid which has least numbers can use.*/
 56         }
 57     if(Min!=0x7fffffff) return pos;
 58     pos.x=-1; return pos;/*No grids can fill in*/
 59 }
 60 
 61 int    Dfs(const int rest)
 62 {
 63     if(rest==0)
 64     {
 65         printf("\n\n\tSolution #%d:  \n\n",++cnt);
 66         Print(Map,C1,C2);
 67         /*too many solutions.*/
 68         if(cnt==5000) return 1;
 69         return 0;
 70     }
 71     struct PII pos=Get_next();
 72     if(pos.x==-1) return 0;
 73     int i,list[11],top=0;
 74     /*find numbers not used, and save then in 'list'.*/
 75     for(i=1;i<=9;++i)
 76         if(!F[pos.x][pos.y][i]) list[top++]=i;
 77     for(i=0;i<top;++i)
 78     {
 79         /*Mark that number 'i' has been used*/
 80         Map[pos.x][pos.y]=list[i];
 81         Update(pos.x,pos.y,list[i]);
 82         
 83         if(Dfs(rest-1)) return 1;
 84         /*if Dfs retruns 1, there would be too many solutions*/
 85         /*stop searching and return*/
 86         
 87         /*Undo*/
 88         Undo_update(pos.x,pos.y,list[i]);
 89         Map[pos.x][pos.y]=0;
 90     }
 91     return 0;
 92 }
 93 
 94 int main()
 95 {
 96     int op,T=0;
 97     system("cls");
 98     printf("Choose input/output way(1.Keyboard/2.File):  ");
 99     while(1)/*until receiving an expected input.*/
100     {
101         scanf("%d",&op);
102         if(op==1)break;
103         if(op==2)
104         {
105             char File_Name[110];
106             printf("Please input File_Name:  ");
107             scanf("%s",File_Name);
108             printf("\n\tResult Will Save to Result.txt\n\n");
109             printf("\tCalculating....\n\n");
110             freopen(File_Name,"r",stdin);
111             freopen("Result.txt","w",stdout);
112             break;
113         }
114     }
115 
116     while(1)/*Support multi-set test, read to EOF.*/
117     {
118         memset(F,0,sizeof(F));
119         memset(Map,0,sizeof(Map));
120         cnt=0;
121         if(op==1)printf("Please Input 9*9 matrix(0 for space):\n");
122         int i,j,data;
123         
124         /*Read until EOF*/
125         for(i=0;i<9;++i) for(j=0;j<9;++j)
126         {
127             if(!(~scanf("%1d",&data)))
128             /*Only ~(-1)==0 while EOF==-1*/
129             {
130                 if(op==1)system("pause");
131                 fclose(stdin);
132                 return 0;
133             }
134             Map[i][j]=data;
135         }
136 
137         int rest=81;/*Update array 'F'. */
138         for(i=0;i<9;++i) for(j=0;j<9;++j)
139             if(Map[i][j]) Update(i,j,Map[i][j]),rest--;
140 
141         printf("Test Case #%d: ",++T);
142 
143         /*Check if the input is corret.*/
144         int f=0,k;
145         for(i=0;i<9;++i)
146         {
147             for(j=0;j<9;++j)
148             {
149                 if(Map[i][j] && F[i][j][Map[i][j]]>3) { f=1; break; }
150                 if(Map[i][j])continue;
151                 int cc=0;/*Count the numbers can use in (i,j)*/
152                 for(k=1;k<=9;++k)
153                     if(!F[i][j][k])cc++;
154                 if(cc==0) { f=1; break; }
155             }
156             if(j!=9) break;
157         }
158 
159         /*Input error*/
160         if(f)
161         {
162             fprintf(stderr,"\n\t\tError in Test Case #%d: ",T);
163             fprintf(stderr,"Invalid Input.\n");
164             printf("\n\n\tFiled: No Solution Found!\n\n");
165             continue;
166         }
167 
168         /*if a number is known, it will be output as [x]*/
169         /*otherwise, there will not be the square brackets, spaces instead.*/
170         for(i=0;i<9;++i)
171             for(j=0;j<9;++j)
172                 if(Map[i][j])C1[i][j]='[',C2[i][j]=']';
173                 else C1[i][j]=C2[i][j]=' ';
174 
175         int t1=clock();/*Timer*/
176         if(Dfs(rest))
177             /*In case of costing too much time and disk storage,*/
178             /*Dfs(int,int) will find at most 5000 kinds of solution.*/
179         {
180             fprintf(stderr,"\n\t\tError in Test Case #%d: ",T);
181             fprintf(stderr,"Too much solution! Calculation has broken.\n");
182         }
183 
184         if(cnt==0) printf("\n\n\tFiled: No Solution Found!\n\n");
185         else printf("\n\n\t%d Solution Found In %ldms.\n\n",cnt,1000*(clock()-t1)/CLOCKS_PER_SEC);
186     }
187 
188     return 0;
189 }
View Code

输出函数print_sudoku.h(上面两篇代码公用):

 1 /*
 2     *This head defines the function to print a Sudoku by a 9*9 array
 3     *Map[9][9] is the munber matrix,C1,C2 are the characters to print on the left and
 4     *  right side of the numbers.
 5 */
 6 #include <stdio.h>
 7 /*Function for printing the Sudoku table.*/
 8 void    Print(int Map[9][9],char C1[9][9],char C2[9][9])
 9 {
10     printf("\t  || A | B | C || D | E | F || G | H | I ||\n");
11     printf("\t==#########################################\n");
12     printf("\t1-##%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c##\n",
13         C1[0][0], Map[0][0], C2[0][0], C1[0][1], Map[0][1], C2[0][1],
14         C1[0][2], Map[0][2], C2[0][2], C1[0][3], Map[0][3], C2[0][3],
15         C1[0][4], Map[0][4], C2[0][4], C1[0][5], Map[0][5], C2[0][5],
16         C1[0][6], Map[0][6], C2[0][6], C1[0][7], Map[0][7], C2[0][7],
17         C1[0][8], Map[0][8], C2[0][8]);
18     printf("\t--##---+---+---||---+---+---||---+---+---##\n");
19     printf("\t2-##%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c##\n",
20         C1[1][0], Map[1][0], C2[1][0], C1[1][1], Map[1][1], C2[1][1],
21         C1[1][2], Map[1][2], C2[1][2], C1[1][3], Map[1][3], C2[1][3],
22         C1[1][4], Map[1][4], C2[1][4], C1[1][5], Map[1][5], C2[1][5],
23         C1[1][6], Map[1][6], C2[1][6], C1[1][7], Map[1][7], C2[1][7],
24         C1[1][8], Map[1][8], C2[1][8]);
25     printf("\t--##---+---+---||---+---+---||---+---+---##\n");
26     printf("\t3-##%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c##\n",
27         C1[2][0], Map[2][0], C2[2][0], C1[2][1], Map[2][1], C2[2][1],
28         C1[2][2], Map[2][2], C2[2][2], C1[2][3], Map[2][3], C2[2][3],
29         C1[2][4], Map[2][4], C2[2][4], C1[2][5], Map[2][5], C2[2][5],
30         C1[2][6], Map[2][6], C2[2][6], C1[2][7], Map[2][7], C2[2][7],
31         C1[2][8], Map[2][8], C2[2][8]);
32     printf("\t==##===========++===========++===========##\n");
33     printf("\t4-##%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c##\n",
34         C1[3][0], Map[3][0], C2[3][0], C1[3][1], Map[3][1], C2[3][1],
35         C1[3][2], Map[3][2], C2[3][2], C1[3][3], Map[3][3], C2[3][3],
36         C1[3][4], Map[3][4], C2[3][4], C1[3][5], Map[3][5], C2[3][5],
37         C1[3][6], Map[3][6], C2[3][6], C1[3][7], Map[3][7], C2[3][7],
38         C1[3][8], Map[3][8], C2[3][8]);
39     printf("\t--##---+---+---||---+---+---||---+---+---##\n");
40     printf("\t5-##%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c##\n",
41         C1[4][0], Map[4][0], C2[4][0], C1[4][1], Map[4][1], C2[4][1],
42         C1[4][2], Map[4][2], C2[4][2], C1[4][3], Map[4][3], C2[4][3],
43         C1[4][4], Map[4][4], C2[4][4], C1[4][5], Map[4][5], C2[4][5],
44         C1[4][6], Map[4][6], C2[4][6], C1[4][7], Map[4][7], C2[4][7],
45         C1[4][8], Map[4][8], C2[4][8]);
46     printf("\t--##---+---+---||---+---+---||---+---+---##\n");
47     printf("\t6-##%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c##\n",
48         C1[5][0], Map[5][0], C2[5][0], C1[5][1], Map[5][1], C2[5][1],
49         C1[5][2], Map[5][2], C2[5][2], C1[5][3], Map[5][3], C2[5][3],
50         C1[5][4], Map[5][4], C2[5][4], C1[5][5], Map[5][5], C2[5][5],
51         C1[5][6], Map[5][6], C2[5][6], C1[5][7], Map[5][7], C2[5][7],
52         C1[5][8], Map[5][8], C2[5][8]);
53     printf("\t==##===========++===========++===========##\n");
54     printf("\t7-##%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c##\n",
55         C1[6][0], Map[6][0], C2[6][0], C1[6][1], Map[6][1], C2[6][1],
56         C1[6][2], Map[6][2], C2[6][2], C1[6][3], Map[6][3], C2[6][3],
57         C1[6][4], Map[6][4], C2[6][4], C1[6][5], Map[6][5], C2[6][5],
58         C1[6][6], Map[6][6], C2[6][6], C1[6][7], Map[6][7], C2[6][7],
59         C1[6][8], Map[6][8], C2[6][8]);
60     printf("\t--##---+---+---||---+---+---||---+---+---##\n");
61     printf("\t8-##%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c##\n",
62         C1[7][0], Map[7][0], C2[7][0], C1[7][1], Map[7][1], C2[7][1],
63         C1[7][2], Map[7][2], C2[7][2], C1[7][3], Map[7][3], C2[7][3],
64         C1[7][4], Map[7][4], C2[7][4], C1[7][5], Map[7][5], C2[7][5],
65         C1[7][6], Map[7][6], C2[7][6], C1[7][7], Map[7][7], C2[7][7],
66         C1[7][8], Map[7][8], C2[7][8]);
67     printf("\t--##---+---+---||---+---+---||---+---+---##\n");
68     printf("\t9-##%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c||%c%d%c|%c%d%c|%c%d%c##\n",
69         C1[8][0], Map[8][0], C2[8][0], C1[8][1], Map[8][1], C2[8][1],
70         C1[8][2], Map[8][2], C2[8][2], C1[8][3], Map[8][3], C2[8][3],
71         C1[8][4], Map[8][4], C2[8][4], C1[8][5], Map[8][5], C2[8][5],
72         C1[8][6], Map[8][6], C2[8][6], C1[8][7], Map[8][7], C2[8][7],
73         C1[8][8], Map[8][8], C2[8][8]);
74     printf("\t==#########################################\n");
75     return ;
76 }
View Code

附加几个测试数据:

 1 000010054
 2 800000000
 3 000000000
 4 650400000
 5 000002730
 6 000000000
 7 210000800
 8 700000300
 9 000350000
10 
11 008900070
12 053000000
13 000000000
14 760100090
15 200000000
16 000080000
17 000020805
18 400007000
19 000000300
20 
21 800000000
22 003600000
23 070090200
24 050007000
25 000045700
26 000100030
27 001000068
28 008500010
29 090000400
30 
31 000064000
32 000000000
33 910000070
34 700000000
35 000900010
36 406008000
37 000000408
38 000002600
39 500100000
40 
41 205600000
42 100007003
43 008000000
44 040031000
45 000000820
46 000090000
47 000000000
48 030000001
49 000200500
50 
51 005300000
52 800000020
53 070010500
54 400005300
55 010070006
56 003200080
57 060500009
58 004000030
59 000009700
60 
61 005890060
62 001000005
63 400010200
64 700900006
65 500000002
66 060027050
67 008005001
68 040709500
69 200000300
View Code

 

推荐阅读