ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

递归解数独

2021-06-27 14:34:01  阅读:213  来源: 互联网

标签:digit 递归 column int textit line 解数 block


我们可以考虑按照「行优先」的顺序依次枚举每一个空白格中填的数字,通过递归 + 回溯的方法枚举所有可能的填法。当递归到最后一个空白格后,如果仍然没有冲突,说明我们找到了答案;在递归的过程中,如果当前的空白格不能填下任何一个数字,那么就进行回溯。

由于每个数字在同一行、同一列、同一个九宫格中只会出现一次,因此我们可以使用 \textit{line}[i]line[i],\textit{column}[j]column[j],\textit{block}[x][y]block[x][y] 分别表示第 ii 行,第 jj 列,第 (x, y)(x,y) 个九宫格中填写数字的情况。在下面给出的三种方法中,我们将会介绍两种不同的表示填写数字情况的方法。

九宫格的范围为 0 \leq x \leq 20≤x≤2 以及 0 \leq y \leq 20≤y≤2。
具体地,第 ii 行第 jj 列的格子位于第 (\lfloor i/3 \rfloor, \lfloor j/3 \rfloor)(⌊i/3⌋,⌊j/3⌋) 个九宫格中,其中 \lfloor u \rfloor⌊u⌋ 表示对 uu 向下取整。

由于这些方法均以递归 + 回溯为基础,算法运行的时间(以及时间复杂度)很大程度取决于给定的输入数据,而我们很难找到一个非常精确的渐进紧界。因此这里只给出一个较为宽松的渐进复杂度上界 O(9^{9 \times 9})O(9 
9×9
 ),即最多有 9 \times 99×9 个空白格,每个格子可以填 [1, 9][1,9] 中的任意整数。

方法一:递归
思路

最容易想到的方法是用一个数组记录每个数字是否出现。由于我们可以填写的数字范围为 [1, 9][1,9],而数组的下标从 00 开始,因此在存储时,我们使用一个长度为 99 的布尔类型的数组,其中 ii 个元素的值为 \text{True}True,当且仅当数字 i+1i+1 出现过。例如我们用 \textit{line}[2][3] = \text{True}line[2][3]=True 表示数字 44 在第 22 行已经出现过,那么当我们在遍历到第 22 行的空白格时,就不能填入数字 44。

算法

我们首先对整个数独数组进行遍历,当我们遍历到第 ii 行第 jj 列的位置:

如果该位置是一个空白格,那么我们将其加入一个用来存储空白格位置的列表中,方便后续的递归操作;

如果该位置是一个数字 xx,那么我们需要将 \textit{line}[i][x-1]line[i][x−1],\textit{column}[j][x-1]column[j][x−1] 以及 \textit{block}[\lfloor i/3 \rfloor][\lfloor j/3 \rfloor][x-1]block[⌊i/3⌋][⌊j/3⌋][x−1] 均置为 \text{True}True。

当我们结束了遍历过程之后,就可以开始递归枚举。当递归到第 ii 行第 jj 列的位置时,我们枚举填入的数字 xx。根据题目的要求,数字 xx 不能和当前行、列、九宫格中已经填入的数字相同,因此 \textit{line}[i][x-1]line[i][x−1],\textit{column}[j][x-1]column[j][x−1] 以及 \textit{block}[\lfloor i/3 \rfloor][\lfloor j/3 \rfloor][x-1]block[⌊i/3⌋][⌊j/3⌋][x−1] 必须均为 \text{False}False。

当我们填入了数字 xx 之后,我们要将上述的三个值都置为 \text{True}True,并且继续对下一个空白格位置进行递归。在回溯到当前递归层时,我们还要将上述的三个值重新置为 \text{False}False。

代码

C++JavaPython3GolangC

bool line[9][9];
bool column[9][9];
bool block[3][3][9];
bool valid;
int* spaces[81];
int spacesSize;

void dfs(char** board, int pos) {
    if (pos == spacesSize) {
        valid = true;
        return;
    }

    int i = spaces[pos][0], j = spaces[pos][1];
    for (int digit = 0; digit < 9 && !valid; ++digit) {
        if (!line[i][digit] && !column[j][digit] && !block[i / 3][j / 3][digit]) {
            line[i][digit] = column[j][digit] = block[i / 3][j / 3][digit] = true;
            board[i][j] = digit + '0' + 1;
            dfs(board, pos + 1);
            line[i][digit] = column[j][digit] = block[i / 3][j / 3][digit] = false;
        }
    }
}

void solveSudoku(char** board, int boardSize, int* boardColSize) {
    memset(line, false, sizeof(line));
    memset(column, false, sizeof(column));
    memset(block, false, sizeof(block));
    valid = false;
    spacesSize = 0;

    for (int i = 0; i < 9; ++i) {
        for (int j = 0; j < 9; ++j) {
            if (board[i][j] == '.') {
                spaces[spacesSize] = malloc(sizeof(int) * 2);
                spaces[spacesSize][0] = i;
                spaces[spacesSize++][1] = j;
            } else {
                int digit = board[i][j] - '0' - 1;
                line[i][digit] = column[j][digit] = block[i / 3][j / 3][digit] = true;
            }
        }
    }

    dfs(board, 0);
}

标签:digit,递归,column,int,textit,line,解数,block
来源: https://blog.csdn.net/weixin_53690010/article/details/118274017

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有