解決標準 9×9 數獨。空格用 '.' 表示。保證有唯一解。需原地修改棋盤。
bit i = 1 表示數字 i+1 可用。row[9], col[9], box[9] 初始全 0x1FF (511)(9 位全 1)。d → mask ^= (1 << (d-1)) 清零對應位。mask & -mask 取最低位 1,或預計算 bits[512] 列表。class Solution {
int row[9], col[9], box[9];
vector<pair<int,int>> emptyCells;
bool solved = false;
void solveSudoku(vector<vector<char>>& board) {
// 初始化掩碼
fill(begin(row), end(row), 0x1FF);
fill(begin(col), end(col), 0x1FF);
fill(begin(box), end(box), 0x1FF);
emptyCells.clear();
for (int i = 0; i < 9; ++i)
for (int j = 0; j < 9; ++j)
if (board[i][j] == '.')
emptyCells.emplace_back(i, j);
else {
int d = board[i][j] - '1';
int mask = 1 << d;
row[i] ^= mask;
col[j] ^= mask;
box[(i/3)*3 + j/3] ^= mask;
}
dfs(board, 0);
}
void dfs(vector<vector<char>>& board, int idx) {
if (idx == emptyCells.size()) { solved = true; return; }
auto [r, c] = emptyCells[idx];
int b = (r/3)*3 + c/3;
int avail = row[r] & col[c] & box[b]; // 可用數字掩碼
while (avail) {
int bit = avail & -avail; // 取最低位 1
avail ^= bit; // 從可用集移除
int d = __builtin_ctz(bit); // 數字 0-8
board[r][c] = '1' + d;
row[r] ^= bit; col[c] ^= bit; box[b] ^= bit;
dfs(board, idx + 1);
if (solved) return;
// 回溯
row[r] ^= bit; col[c] ^= bit; box[b] ^= bit;
board[r][c] = '.';
}
}
};
| 技巧 | 效果 |
|---|---|
| 位掩碼 O(1) 合法性檢查 | 避免 3×9 循環掃描 |
| 預收集空格列表 | 避免遞歸中掃描棋盤 |
avail & -avail 取最低位 | 無分支枚舉候選數字 |
__builtin_ctz 位轉數字 | 單指令完成,極快 |
全局 solved 標記 | 找到解後立即展開回溯,不繼續搜索 |
board[i][j] == 'p':p 是變量而非字面量,應為 board[i][j] == d 或 bit 掩碼判斷。(i/3)*3 + j/3 而非 i/3 + j/3*3。^= bit 恢復,否則後續分支錯誤。solved 提前返回:找到解後繼續搜索導致 TLE 或覆蓋答案。9^81,實際經位掩碼剪枝 + 唯一解保證,< 10^5 節點訪問。Sudoku Solver 是回溯法 + 位掩碼優化的教科書級應用。關鍵在於將「行/列/宮約束」編碼為 9 位整數,利用位運算實現 O(1) 合法性檢查與候選枚舉。掌握此模板,可直接應用於 N 皇後、圖著色、十字填字等精確覆蓋問題。現代 C++ 的 __builtin_ctz、bit & -bit 等位運算技巧,使代碼既簡潔又極致高效。