LeetCode题目:37. Sudoku Solver

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/MyCodecOdecoDecodE/article/details/78314280

LeetCode题目:37. Sudoku Solver

原题链接:https://leetcode.com/problems/sudoku-solver/description/

解题思路:

(现实生活中都不会怎么解数独,怎么写代码= =,于是用比较暴力的方法实现了。)

解数独分为两部分:

1.利用唯一余数法解出答案

同列,同行,同宫出现的数字排除后,该格只能填一个数,即为答案。

2.递归猜测答案

如果已经不存在1中的方格,选择一个方格递归所有可能填的值,直到得出答案。

核心思想:

  • 唯一取余法思想:

    1. 用bitset数组(二进制流数组)储存某一行,某一列或者某一宫已出现的数字,每一个二进制位代表某个数字是否出现过。

      所以,某一格ij能填写的数字为 (第i行的二进制流 | 第j列的二进制流 | 第t宫的二进制流) 0的位置,其中t=i / 3 * 3 + j / 3

    2. 遍历数独所有方格,当一个方格只有唯一解的时候,填上答案,修改bitset数组。

    3. 用2的方法递归同行,同列,同宫的所有方格。如果用唯一余数法没有找到可填写的数字,则此时整个数独已经没有能填写的方格。此时只能递归猜测答案。

  • 递归猜测答案思想:

    1. 基本情况:

      ​ 如果出现没有解的可填写方格,则这种猜测失败。

      ​ 如果所有方格都填写上了解,则猜测成功,当前矩阵即为答案,把当前矩阵返回上一层。

    2. 递归情况:

      ​ 用唯一取余法填写已经确定的方格,直到所有方格都无法确定。

      ​ 任取一未填写方格,分别递归所有可能的取值,并得到返回值,如果出现返回值为true,代表已经找到答案,返回答案矩阵。

代码细节:

  1. 第i行j列的方格在i / 3 * 3 + j / 3宫中
  2. 猜测的时候,复制所有环境进行下一轮递归,如果得到正确答案,将复制值代替原始值即可。
  3. 为了简化复杂度,选出可能性最少的方格再进行猜测填写。

坑点:

  1. 光使用唯一余数法并不能解出答案。

代码:

// #include <bitset>
// 从左往右,从上到下分为9宫,在这里为0-8 
int getSquareNumber(int i, int j) {
    return i / 3 * 3 + j / 3;
}

// 得到i,j位方格的Bitset 
bitset<9> getSquareBitset(int i, int j, bitset<9>* row, bitset<9>* col, bitset<9>* squ) {
    return row[i] | col[j] | squ[getSquareNumber(i, j)];
}

// 判断该位置是否只有唯一解 
bool hasUniqueAnswer(int i, int j, bitset<9>* row, bitset<9>* col, bitset<9>* squ) {
    return (getSquareBitset(i, j, row, col, squ)).count() == 8;
}

// 判断该位置是否无解 
bool hasNoAnswer(int i, int j, bitset<9>* row, bitset<9>* col, bitset<9>* squ) {
    return (getSquareBitset(i, j, row, col, squ)).count() == 9;
}

// 返回当前数独的状态,-1代表已经无解,0代表可能有解,1代表已经解出 
int getProblemState(vector<vector<char>>& board, bitset<9>* row,
                    bitset<9>* col, bitset<9>* squ) {
    int state = 1;
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            // 若不存在'.',意为解出答案 
            if (board[i][j] == '.') {
                state = 0;
                // 如果有一个'.'无解,则无解 
                if (hasNoAnswer(i, j, row, col, squ))
                    return -1;
            }
        }
    }
    return state;
}

// 根据数独设置rows,cols,squs和remainEmptyNum
void setEnvironment(vector<vector<char>>& board, bitset<9>* row,
                    bitset<9>* col, bitset<9>* squ, int &remainEmptyNum) {
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            if (board[i][j] == '.') {
                remainEmptyNum++;
                continue;
            }
            int num = board[i][j] - '1';
            // 设置行 
            row[i].set(num);
            // 设置列 
            col[j].set(num);
            // 设置宫
            squ[getSquareNumber(i, j)].set(num);
        }
    } 
}

// 填充唯一解的方格,并递归寻找同行,同列,同宫的方格 
void recurSolveUniqueAnswer(vector<vector<char>>& board, int i, int j,
                    bitset<9>* row, bitset<9>* col, bitset<9>* squ) {
    int seqIndex = getSquareNumber(i, j);
    bitset<9> temp = row[i] | col[j] | squ[seqIndex]; 

    // 查找该值 
    int t;
    for (t = 0; t < 9; t++)
        if (!temp.test(t))  break;

    // 修改bitsets,board,remainEmptyNum 
    row[i].set(t);
    col[j].set(t);
    squ[seqIndex].set(t);
    board[i][j] = t + '1';

    // 递归求解 
    for (t = 0; t < 9; t++) {
        // 同行递归 
        if (board[i][t] == '.' && hasUniqueAnswer(i, t, row, col, squ))
            recurSolveUniqueAnswer(board, i, t, row, col, squ);
        // 同列递归 
        if (board[t][j] == '.' && hasUniqueAnswer(t, j, row, col, squ))
            recurSolveUniqueAnswer(board, t, j, row, col, squ);
        // 同宫递归
        int rowIndex = seqIndex / 3 * 3 + t / 3;
        int colIndex = seqIndex % 3 * 3 + t % 3;
        if (board[rowIndex][colIndex] == '.' && hasUniqueAnswer(rowIndex, colIndex, row, col, squ)) 
            recurSolveUniqueAnswer(board, rowIndex, colIndex, row, col, squ);
    }
}

// 递归求解问题,并返回是否有解 
bool recurSolveProblem(vector<vector<char>>& board,
                bitset<9>* row, bitset<9>* col, bitset<9>* squ) {


    // 找出是否存在唯一解,若有,进行求解并填充 
    for (int i = 0; i < 9; i++)
        for (int j = 0; j < 9; j++)
            if (board[i][j] == '.' && hasUniqueAnswer(i, j, row, col, squ))
                recurSolveUniqueAnswer(board, i, j, row, col, squ);

    // 基本情况:已经出现结果 
    int state = getProblemState(board, row, col, squ);
    if (state != 0)  return state == 1;

    // 递归情况:找到最少可能性的方格对所有可能性进行递归

    // 找到最小可能性的方格 
    int r, c;
    bitset<9> minBitset;
    minBitset.set(); 
    bitset<9> temp;
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            if (board[i][j] == '.') {
                temp = getSquareBitset(i, j, row, col, squ);
                if (temp.count() < minBitset.count()) {
                    minBitset = temp;
                    r = i;
                    c = j;
                }
            }
        }
    }

    // 对其中可能出现的情况依次递归 
    for (int i = 0; i < 9; i++) {
        if (!minBitset.test(i)) {
            // 复制初始环境
            vector<vector<char>> cboard = board;
            bitset<9> crow[9], ccol[9], csqu[9];
            for (int i = 0; i < 9; i++) {
                crow[i] = row[i];
                ccol[i] = col[i];
                csqu[i] = squ[i];
            }

            // 假设该位填写数字(i + 1) 
            crow[r].set(i);
            ccol[c].set(i);
            csqu[getSquareNumber(r, c)].set(i);
            cboard[r][c] = i + '1';

            // 递归解出题目 
            if (recurSolveProblem(cboard, crow, ccol, csqu)) {
                // 返回true,并复制答案 
                board.clear();
                board = cboard;
                return true;
            }
        }
    }

    // 遍历所有可能都没有得出答案,则错误发生在之前的步骤 
    return false;
}

void solveSudoku(vector<vector<char>>& board) {
    int remainEmptyNum;                // 剩下未填写的格子
    bitset<9> row[9], col[9], squ[9];  // 用于表示第i行,第i列,第i宫已填写数字情况
                                       // 第j个bit为1,代表数字j已填写

    // 初始化条件 
    setEnvironment(board, row, col, squ, remainEmptyNum);

    // 递归寻找解
    recurSolveProblem(board, row, col, squ);
}

猜你喜欢

转载自blog.csdn.net/MyCodecOdecoDecodE/article/details/78314280