数独(sudoku),是一个填数字的游戏,规则简单,上到老爷爷老奶奶,下至小学生,都可以去解它,放松益脑。

一直以来就特别喜欢数独,第一次是从老爸手机上看到的,也做过不少题目。在初中的时候上发过了一本书,书的后面就有一个数独的题目,我是班上第一个也是唯一一个解出来的,十分骄傲。

最近学习了算法,发现里面的n皇后问题和数独特别的相似,感觉都可以使用回溯法在解空间树经行广度优先搜索。这种方法类似于穷举法,但并不是单纯的列举全部可能,不断的有剪枝函数减去不满足条件的子树。看到网上说一种叫做跳舞链的方法,下面的代码并不是采用这种方法,以后如果有时间可以去试一试。而我使用的是回溯法,使用语言是c语言。闲话就说到这里,下面分析一下代码。

 

核心内容:树的深度优先遍历

 

 

 

把数独想象成一个具有81层的树,然后在树上搜索剪枝。

 

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 //定义一个二维数组sudo[9][9]
  5  
  6  /*
  7 int sudo[9][9]={
  8                     {    5,7,6,     -1,4,8,     1,3,-1},
  9                     {  -1,3,-1,    -1,-1,5,    -1,8,-1},
 10                     {   8,2,-1,     3,-1,9,    -1,-1,7},
 11             
 12                     {   1,-1,9,    -1,-1,-1,     -1,-1,8},
 13                     {   -1,-1,3,   5,9,1,      6,-1,-1},
 14                     {   7,-1,-1,    -1,-1,-1,   4,-1,1},
 15             
 16                     {   2,-1,-1,    1,-1,3,     -1,4,5},
 17                     {   -1,1,-1,    4,-1,-1,    -1,2,-1},
 18                     {   -1,4,7,     9,5,-1,     8,1,6,}    
 19             };
 20             */
 21     //测试数据1
 22     int sudo[9][9]={
 23                     {    1,0,0,    0,5,0,     0,0,0},
 24                     {   4,0,2,     0,0,1,      0,3,0},
 25                     {   8,0,0,     0,0,6,      0,1,0},
 26             
 27                     {   0,0,0,    0,1,7,     6,0,0},
 28                     {   0,0,6,      5,0,2,     4,0,0},
 29                     {   0,0,7,    4,6,0,   0,0,0},
 30             
 31                     {   0,4,0,    9,0,0,     0,0,1},
 32                     {   0,9,0,    6,0,0,     7,0,5},
 33                     {   0,0,0,     0,3,0,     0,0,0}    
 34             };
 35     
 36      
 37 int backtrack(int s[9][9],int row,int col);
 38 //使用回溯法对解空间数经行深度搜索该函数就是深度搜索的函数 
 39 
 40 void showsudo();
 41 //打印当前的数独中各个格子的值 
 42 
 43 int place(int s[9][9],int t,int j);
 44 //剪枝函数 
 45 
 46 int justIsRepeatIN9(int s[9][9],int x,int y,int m,int n);
 47 //剪枝函数中检查3*3小块合法的的函数 
 48 
 49 
 50 //从二维数组的左上角开始,向右搜索
 51 //row表示行号,col表示列号 
 52 int backtrack(int s[9][9],int row,int col){
 53     
 54     if(row == 9)  
 55         return 1;
 56         //最后一个,在这之前是s[8][8];递归到最深处;成功返回 
 57      
 58     if(s[row][col] != 0){
 59         if(col == 8){
 60            row++;
 61            col=0;
 62         }
 63         else    
 64             col++;
 65         return backtrack(s,row,col);
 66     }//如果 s[row][col]本来就有数据,需要跳过 ,分析下一个 
 67     
 68     //  s[row][col]是空的,则放入1~9 
 69     for(int k=1;k<10;k++){ 
 70         s[row][col]=k;
 71         if(place(s,row,col)) {
 72             //判断是否合法,如果合法就分析下一个 
 73             if(col == 8){
 74                 if(backtrack(s,row+1,0)){
 75                     return 1;
 76                   }
 77             }
 78              else{
 79                   if(backtrack(s,row,col+1)){
 80                       return 1;
 81                   }
 82              }    
 83         }    
 84         s[row][col] = 0;
 85         //在后面的某次中发现,所有的数都有冲突,则前面的数放错了,所以需要恢复 
 86     }
 87     //从1~9没有找到合适的数放入,之前的出错了 
 88     return 0;
 89 }
 90 
 91 //判断整个数独是否合法 
 92 int place(int s[9][9],int t,int j)
 93 {
 94     for(int i=0;i<9;i++)
 95     {
 96         if(s[t][i] == s[t][j] && i!=j){
 97             return 0 ;
 98         }//判断行上没有冲突     
 99         if(s[i][j] == s[t][j] && i!=t){
100             return 0 ;
101         }//判断列上没有冲突
102     }
103     
104     //判断块中是否有重复的 
105     int x=t%3;
106     int y=j%3;
107         /*
108         00 01 02 
109         10 11 12 
110         20 21 22 
111         */ 
112     if(x == 0 && y == 0){
113         if(justIsRepeatIN9(s,t,j,t,j)){
114             return 1;
115         }
116         else{
117             return 0; 
118         }
119     }
120     
121     if(x == 0 && y == 1){
122         if(justIsRepeatIN9(s,t,j-1,t,j)){
123             return 1;
124         }
125         else{
126             return 0; 
127         }
128     }
129     
130     if(x == 0 && y == 2){
131         if(justIsRepeatIN9(s,t,j-2,t,j)){
132             return 1;
133         }
134         else{
135             return 0; 
136         }
137     }
138     
139     if(x == 1 && y == 0){
140         if(justIsRepeatIN9(s,t-1,j,t,j)){
141             return 1;
142         }
143         else{
144             return 0; 
145         }
146     }
147     
148     if(x == 1 && y == 1){
149         if(justIsRepeatIN9(s,t-1,j-1,t,j)){
150             return 1;
151         }
152         else{
153             return 0; 
154         }
155     }
156 
157     if(x == 1 && y == 2){
158         if(justIsRepeatIN9(s,t-1,j-2,t,j)){
159             return 1;
160         }
161         else{
162             return 0; 
163         }
164     }
165     
166     if(x == 2 && y == 0){
167         if(justIsRepeatIN9(s,t-2,j,t,j)){
168             return 1;
169         }
170         else{
171             return 0; 
172         }
173     }
174     
175         if(x == 2 && y == 1){
176         if(justIsRepeatIN9(s,t-2,j-1,t,j)){
177             return 1;
178         }
179         else{
180             return 0; 
181         }
182     }
183     
184         if(x == 2 && y == 2){
185         if(justIsRepeatIN9(s,t-2,j-2,t,j)){
186             return 1;
187         }
188         else{
189             return 0; 
190         }
191     }    
192 }
193 
194 int justIsRepeatIN9(int s[9][9],int x,int y,int m,int n){
195     //判断一个九宫格中是否有重复的数
196     //其中s是数组,x是起始的位置,y是起始是位置
197     //因为之前的数据都是有序的,并不需要判断其他的是否合法,只要判断指定的数 
198     //只要知道特殊的 
199     //m,n是需要判断的内容 
200     for(int i=0;i<3;i++){
201         for(int j=0;j<3;j++){
202             if(s[x+i][y+j] == s[m][n] && x+i != m && y+j != n){
203                 return 0;
204             } 
205         }
206     }
207     return 1;
208 }
209  
210  //打印数独中各个格子的值 
211  void showsudo()
212  {
213      for(int i=0;i<9;i++){
214         for(int j=0;j<9;j++)
215         {
216             printf("%2d  ",sudo[i][j]) ;
217             if(j%3 == 2)
218             {
219                 printf("  ");
220             }
221          } 
222         printf("\n");
223         if(i%3 == 2){
224             printf("\n");
225         }
226      }
227     printf("\n"); 
228  }
229  
230  int main()
231  {
232      showsudo();
233      backtrack(sudo,0,0) ;
234      showsudo();
235      return 0;
236  }

 

posted on 2017-12-31 14:24  风流倜傥的小花生  阅读(1672)  评论(1编辑  收藏  举报