简单数独的DFS求解

数独DFS求解

  • 简单数独的DFS求解
    • 1. 问题
    • 2.求解
      • 2.1 数据结构设计
      • 2.2 算法设计
      • 2.3 主要代码
        • 1.数独递归处理函数 sudoku_try
        • 2.检测函数 test
    • 3.完整代码

简单数独的DFS求解

1. 问题

给出9×9的标准数独,使用C语言编程完成这个数独的求解。

数独数独(shù dú)是源自18世纪瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复 [1] 。

数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”。

简单数独的DFS求解_第1张图片

2.求解

2.1 数据结构设计

数独的定义决定了在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这样的预定义表示数独大小。

2.2 算法设计

本案中可以暴力1-9从最左上角开始逐个尝试,但是关键问题是如何使用代码控制当发现尝试的数字不符合要求,回退到上一个数字的分析状态,然后尝试上一个数字的其他情况。其实这种情况是典型的DFS应用场景。

也就是说在每一个需要填写数字的位置,可以1-9逐个尝试 ,而每一个尝试又是可以递归成为对下一个位置1-9数字的尝试。

梳理一下算法如下:

  1. 判断是否已经将所有空格填满,如果填满输出数独然后直接退出程序。
  2. 判断当前需要处理的数独格子中,是否为0。
    • 如果为0,表示当前格子是一个需要填入数字的空格,那么可以1-9尝试填入一个数字,并验证是否满足要求。
      • 如果满足要求,那么暂时使用这个结果,并对下一个格子使用本算法递归分析。
      • 如果所有1-9都不能满足,那么表明上一次填入的数字是错误的,所以要回溯到上一次分析状态中(这里实际上是可以通过递归函数的返回实现)。注意:由于前面的尝试过程中,已经在数独中尝试填写了各种数字,这时候回退上一层调用前,必须先恢复原来状态,也就是将单元格设置为0。
    • 如果为1,表示当前单元格中原来就有数字,不需要我们处理,那么可以跳过这一个,处理下面一个格子。

2.3 主要代码

1.数独递归处理函数 sudoku_try

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行代码含义,有助于理解。

2.检测函数 test

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;
}

3.完整代码

#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");
    }
}

本例中代码运行结果如下:
简单数独的DFS求解_第2张图片

可以使用以上代码,试试 https://www.sudoku.name/index-cn.php 网站上的数独,算一下吧

你可能感兴趣的:(教学内容,深度优先,算法)