class Solution { private: int row[9]; int col[9]; int blk[9]; public: void solveSudoku(vector<vector<char> > &board) { if (board.empty() || board[0].empty()) return; initBits(board); dfs(board, 0, 0); } bool dfs(vector<vector<char> >& board, int ridx, int cidx) { if (ridx == 9) { return true; // a valid solution will reach it } char ch = board[ridx][cidx]; bool nr = cidx == 8; int blk_id = ridx/3*3 + cidx/3; if (ch != '.') { return dfs(board, ridx + nr, nr ? 0 : (cidx+1)); } for (int k=1; k<=9; k++) { int msk = 0x1<<(k-1); if ((msk|row[ridx]) == row[ridx] || (msk|col[cidx]) == col[cidx] || (msk|blk[blk_id]) == blk[blk_id]) { // this means that in a column or row or block (where current (ridx, cidx) fall) // the current number has already been used, so give up this try continue; } setBits(ridx, cidx, blk_id, msk); board[ridx][cidx] = k + '0'; if (dfs(board, ridx + nr, nr ? 0 : (cidx+1))) return true; board[ridx][cidx] = ch; unsetBits(ridx, cidx, blk_id, msk); } return false; } void setBits(int r, int c, int b, int msk) { row[r] |= msk; col[c] |= msk; blk[b] |= msk; } void unsetBits(int r, int c, int b, int msk) { row[r] &= ~msk; col[c] &= ~msk; blk[b] &= ~msk; } void initBits(vector<vector<char> > &board) { memset(row, 0, sizeof(row)); memset(col, 0, sizeof(col)); memset(blk, 0, sizeof(blk)); int rm, cm, bm; for (int i=0; i<9; i++) { int base = i / 3 * 3; for (int j=0; j<9; j++) { char ch = board[i][j]; if (ch == '.') continue; int msk = 0x1<<(ch - '0' - 1); int blk_idx = base + j / 3; setBits(i, j, blk_idx, msk); } } } };
以前一直没练算法,连dfs也没写过几个,最多就是二叉树的深度优先遍历,哎。。。那就练起吧!
这题用dfs说白了就是暴力穷举搜索,在每次尝试的时候需要检查填入尝试的数是否会使当前的数独状态遭到破坏,这里用位操作实现检测,每一行,每一列,每一个3*3的block分别给9个位(代码实际上用了int类型),row,col,blk数组,每个元素表示在其代表的范围内数字的出现情况。row[0]、row[1]...分别代表第一行、第二行上数字的出现情况,col[0]、col[1]...分别代表第一列、第二列上的数字出现情况,blk[0]、blk[1]...代表从左向右、从上往下数第一个、第二个3*3block中的数字出现情况。
mask_digit = 0x1<<(digit-1) // mask for a digit area[0] == mask_digit|area[0] // this digit already used area[0] |= mask_digit // use the digit area[0] &= ~mask_digit // un-use the digit
每个区域(行、列、block)中的数字使用情况的维护如上所示。
代码中的dfs尝试从上至下一行一行的扫过去,或许可以选择从已有数字最多的行或者列或者block进行,通过一个优先队列,每次都可以在数目较多的区域进行尝试,这样的区域由于已有的数字较多可以尝试的次数就可以大大减少,从而加快搜索速度。
参考:
zhuli哥的题解 http://www.cnblogs.com/zhuli19901106/p/3574405.html