[ C语言版 ] 数独计算器 [ 搜索剪枝法 ]
【原创】转载请注明出处。 【浙江大学 程序设计专题】
使用方法:按提示输入方式为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 }
方法二代码:
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 }
输出函数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 }
附加几个测试数据:
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