《编程之美》书上并没有这一节,而是我根据“构造数独”一节推广而来的。上一篇我用矩阵的初等变换来构造数独,这一篇我就用DFS(其实我觉得叫递归+回溯更贴切)来求解数独。
具体的步骤我就不啰嗦了,不了解的可以参考任何一本算法书或数据结构的书。递归求解数独并不像网上一些说人的那么慢(一个比较难的数独也是瞬间就解出来了),更不会有栈溢出,处理不当就另当别论。
这个程序采用文件输入,输入的数据格式为:
800000000
003600000
070090200
050007000
000045700
000100030
001000068
008500010
090000400
其中0表示要填的空。为了操作方便我用一维数组来表示二维数组。
最后要说明的是,当所有都是要填的空时就相当于构造数独了。当然,这个程序只能构造出一种数独,要使每次构造出不同的数独就需要在算法中加入随机因子了。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <memory.h> 4 #include <malloc.h> 5 6 void Print(char *grid); 7 int Next(int index); 8 char *GetAllowable(int index, int *count); 9 void Solve(int index); 10 bool completed = false; //标记是否完成 11 bool nosolution = false; //标记是否无解 12 int startIndex = 0; 13 char solution[81]; //保存数独 14 int main(int argc, char *argv[]) 15 { 16 FILE *file; 17 char ch; 18 int i = 0; 19 20 //从文件读入 21 if ((file = fopen("problem.txt", "r")) == NULL) 22 { 23 exit(0); 24 } 25 26 while ((ch = fgetc(file)) != EOF) 27 { 28 if (ch != \'\n\') 29 { 30 *(solution + i) = ch; 31 i++; 32 } 33 } 34 35 fclose(file); 36 puts("题目:"); 37 Print(solution); 38 startIndex = Next(-1); 39 Solve(startIndex); 40 41 if (!nosolution) //有解 42 { 43 puts("\n解答:"); 44 Print(solution); 45 } 46 else 47 { 48 puts("无解!"); 49 } 50 51 puts(""); 52 system("pause"); 53 return 0; 54 } 55 //输出数独 56 void Print(char *grid) 57 { 58 for (int i = 0; i < 81; ++i) 59 { 60 printf("%2c", grid[i] == \'0\' ? \'_\' : grid[i]); 61 62 if (i % 9 == 8) 63 { 64 printf("\n"); 65 } 66 } 67 } 68 //返回所有在index位置请允许的数字,count为总数 69 char *GetAllowable(int index, int *count) 70 { 71 int r = index / 9; //行 72 int c = index % 9; //列 73 bool f[10]; 74 75 for (int i = 1; i < 10; ++i) 76 { 77 f[i] = true; 78 } 79 80 for (int i = 0; i < 9; ++i) 81 { 82 f[solution[r * 9 + i] - \'0\'] = false; //行 83 f[solution[9 * i + c] - \'0\'] = false; //列 84 f[solution[(r / 3 * 3 + i / 3) * 9 + c / 3 * 3 + i % 3] - \'0\'] = false; //小九宫格 85 } 86 87 int n = 0; 88 89 for (int i = 1; i < 10; ++i) 90 { 91 if (f[i]) 92 { 93 n++; 94 } 95 } 96 97 char *v = NULL; 98 99 if (n > 0) 100 { 101 v = (char *)malloc(sizeof(char) * n); 102 103 if (v == NULL) 104 { 105 exit(0); 106 } 107 108 for (int i = 1, j = 0; i < 10; ++i) 109 if (f[i]) 110 { 111 v[j++] = i + \'0\'; 112 } 113 } 114 115 *count = n; 116 return v; 117 } 118 //求解主函数 119 void Solve(int index) 120 { 121 int n, next; 122 char *v = GetAllowable(index, &n); 123 124 if (n == 0) 125 { 126 return; 127 } 128 129 for (int i = 0; i < n; ++i) 130 { 131 solution[index] = v[i]; 132 133 if ((next = Next(index)) == 81) //完成 134 { 135 completed = true; 136 } 137 else 138 { 139 Solve(next); //递归 140 } 141 142 if (completed == true) 143 { 144 free(v); 145 return; 146 } 147 } 148 149 free(v); 150 solution[index] = \'0\'; 151 152 if (index == startIndex) //无解 153 { 154 nosolution = true; 155 } 156 } 157 //找下一个要填的空格 158 int Next(int index) 159 { 160 while (++index < 81 && solution[index] != \'0\'); 161 162 return index; 163 }