LeetCode题目:37. Sudoku Solver
来源:互联网 发布:优秀的短篇小说知乎 编辑:程序博客网 时间:2024/06/05 15:56
LeetCode题目:37. Sudoku Solver
原题链接:https://leetcode.com/problems/sudoku-solver/description/
解题思路:
(现实生活中都不会怎么解数独,怎么写代码= =,于是用比较暴力的方法实现了。)
解数独分为两部分:
1.利用唯一余数法解出答案
同列,同行,同宫出现的数字排除后,该格只能填一个数,即为答案。
2.递归猜测答案
如果已经不存在1中的方格,选择一个方格递归所有可能填的值,直到得出答案。
核心思想:
唯一取余法思想:
用bitset数组(二进制流数组)储存某一行,某一列或者某一宫已出现的数字,每一个二进制位代表某个数字是否出现过。
所以,某一格ij能填写的数字为 (第i行的二进制流 | 第j列的二进制流 | 第t宫的二进制流) 0的位置,其中t=i / 3 * 3 + j / 3
遍历数独所有方格,当一个方格只有唯一解的时候,填上答案,修改bitset数组。
用2的方法递归同行,同列,同宫的所有方格。如果用唯一余数法没有找到可填写的数字,则此时整个数独已经没有能填写的方格。此时只能递归猜测答案。
递归猜测答案思想:
基本情况:
如果出现没有解的可填写方格,则这种猜测失败。
如果所有方格都填写上了解,则猜测成功,当前矩阵即为答案,把当前矩阵返回上一层。
递归情况:
用唯一取余法填写已经确定的方格,直到所有方格都无法确定。
任取一未填写方格,分别递归所有可能的取值,并得到返回值,如果出现返回值为true,代表已经找到答案,返回答案矩阵。
代码细节:
- 第i行j列的方格在i / 3 * 3 + j / 3宫中
- 猜测的时候,复制所有环境进行下一轮递归,如果得到正确答案,将复制值代替原始值即可。
- 为了简化复杂度,选出可能性最少的方格再进行猜测填写。
坑点:
- 光使用唯一余数法并不能解出答案。
代码:
// #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);}
阅读全文
0 0
- LeetCode题目:37. Sudoku Solver
- LeetCode --- 37. Sudoku Solver
- LeetCode 37.Sudoku Solver
- [Leetcode] 37. Sudoku Solver
- [leetcode] 37. Sudoku Solver
- Leetcode 37. Sudoku Solver
- leetcode 37. Sudoku Solver
- LeetCode 37. Sudoku Solver
- leetcode.37. Sudoku Solver
- LeetCode-37.Sudoku Solver
- leetcode 37. Sudoku Solver
- (Leetcode)37. Sudoku Solver
- [LeetCode] 37. Sudoku Solver
- leetcode 37.Sudoku Solver
- leetcode 37. Sudoku Solver
- leetcode 37. Sudoku Solver
- leetcode-37. Sudoku Solver
- [LeetCode]37. Sudoku Solver
- hashmap的实现和扩展分析
- canvas
- 简单洗牌(打乱52个随机生成的数字)
- Spark GraphX实现Bron–Kerbosch算法-极大团问题
- 算法练习(6) —— 贪心算法
- LeetCode题目:37. Sudoku Solver
- Ⅲ vue2.0 webpack打包
- android开源库BGA----BGAbanner的使用
- 组合模式
- 解决安卓7.0系统写入SD卡权限失败问题
- JavaScript中字符串最常用的几个方法
- 深入理解java虚拟机
- android (序列化) serializable 与 Parcelable
- 卡尔曼滤波 从推导到应用(2)