LeetCode题目:37. Sudoku Solver

来源:互联网 发布:优秀的短篇小说知乎 编辑:程序博客网 时间:2024/06/05 15:56

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和remainEmptyNumvoid 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);}
原创粉丝点击