给出9×9的标准数独,使用C语言编程完成这个数独的求解。
数独数独(shù dú)是源自18世纪瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复 [1] 。
数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”。
数独的定义决定了在C语言中使用一个二维数组就能完美的解决问题,所以这里使用9×9的二维数组存储数独,其中使用0表示需要填入数字的空格。
例如以上数独可以使用如下数组数据表示:
0 6 3 0 0 7 8 0 0
0 1 2 3 0 0 5 0 0
0 0 5 0 0 0 0 1 0
0 5 0 4 0 0 0 0 0
0 3 7 0 0 0 4 9 0
0 0 0 0 0 2 0 6 0
0 8 0 0 0 0 6 0 0
0 0 4 0 0 6 7 8 0
0 0 6 2 0 0 9 3 0
以上0表示数独中需要填入数组的空格
考虑到程序的可扩展性,比如解决超级数独的情况,代码中使用#define N 9
这样的预定义表示数独大小。
本案中可以暴力1-9从最左上角开始逐个尝试,但是关键问题是如何使用代码控制当发现尝试的数字不符合要求,回退到上一个数字的分析状态,然后尝试上一个数字的其他情况。其实这种情况是典型的DFS应用场景。
也就是说在每一个需要填写数字的位置,可以1-9逐个尝试 ,而每一个尝试又是可以递归成为对下一个位置1-9数字的尝试。
梳理一下算法如下:
void sudoku_try(int s[N][N], int x, int y){
int i;
if ( x == N ){ /* 当前处理到N行了,也就是0~N-1都填完了,结束!*/
printf("\n");
print(s); /* 输出数独 */
exit(0); /* 退出程序 */
}
if ( s[x][y] == 0 ) {
for ( i=1; i <= N; i++ ) {
if ( test(s, x, y, i) ) {
s[x][y] = i;
sudoku_try(s, (y+1) / N + x, (y+1) % N);
}
}
s[x][y] = 0; /* 回退到上一层递归调用前,消除前面尝试遗留的数据。*/
}
else{
sudoku_try(s, (y+1) / N + x, (y+1) % N);
}
}
请注意以上代码中第5,15和18行代码含义,有助于理解。
int test(int s[N][N], int x, int y, int k){
int i, j;
for (i = 0; i< N; i++) { /* 分别行列检测是否有k已经存在 */
if ( s[x][i] == k || s[i][y] == k ) return 0;
}
/* 检测各自所在小9宫格 */
for ( i = x / 3 * 3; i < x / 3 * 3 + 3; i++ )
for ( j = y / 3 * 3; j < y / 3 * 3 + 3; j++ )
if ( s[i][j] == k ) return 0;
return 1;
}
#include
#include
#define N 9
int test (int s[N][N], int x, int y, int k);
void print(int s[N][N]);
void sudoku_try(int s[N][N], int x, int y);
int main (int argc, char *argv[])
{
int sudoku[][N] = {
{0, 6, 3, 0, 0, 7, 8, 0, 0},
{0, 1, 2, 3, 0, 0, 5, 0, 0},
{0, 0, 5, 0, 0, 0, 0, 1, 0},
{0, 5, 0, 4, 0, 0, 0, 0, 0},
{0, 3, 7, 0, 0, 0, 4, 9, 0},
{0, 0, 0, 0, 0, 2, 0, 6, 0},
{0, 8, 0, 0, 0, 0, 6, 0, 0},
{0, 0, 4, 0, 0, 6, 7, 8, 0},
{0, 0, 6, 2, 0, 0, 9, 3, 0},
};
print(sudoku);
sudoku_try(sudoku, 0, 0);
return 0;
}
void sudoku_try(int s[N][N], int x, int y){
int i;
if ( x == N ){
printf("\n");
print(s); /* 求一个解后退出,如果需 */
exit(0); /* 要求出所有解,怎么修改?*/
}
if ( s[x][y] == 0 ) {
for ( i=1; i <= N; i++ ) {
if ( test(s, x, y, i) ) {
s[x][y] = i;
sudoku_try(s, (y+1) / N + x, (y+1) % N);
}
}
s[x][y] = 0; /* 请解释语句作用? */
}
else{
sudoku_try(s, (y+1) / N + x, (y+1) % N);
}
}
int test(int s[N][N], int x, int y, int k){
int i, j;
for (i = 0; i< N; i++) {
if ( s[x][i] == k || s[i][y] == k ) return 0;
}
for ( i = x / 3 * 3; i < x / 3 * 3 + 3; i++ )
for ( j = y / 3 * 3; j < y / 3 * 3 + 3; j++ )
if ( s[i][j] == k ) return 0;
return 1;
}
void print(int s[N][N]){
int i, j;
for ( i = 0; i < N; i++ ) {
for ( j = 0; j < N; j++)
printf("%3d", s[i][j]);
printf("\n");
}
}
可以使用以上代码,试试 https://www.sudoku.name/index-cn.php 网站上的数独,算一下吧