LeetCode 52
題目簡述
N皇后問題的變式
難度:困難
描述:
n 皇后問題研究的是如何將 n 個皇后放置在 n×n 的棋盤上,並且使皇后彼此之間不能相互攻擊。
上圖爲 8 皇后問題的一種解法。
給定一個整數 n,返回 n 皇后不同的解決方案的數量。
示例:
輸入: 4
輸出: 2
解釋: 4 皇后問題存在如下兩個不同的解法。
[
[".Q..", // 解法 1
"...Q",
"Q...",
"..Q."],
["..Q.", // 解法 2
"Q...",
"...Q",
".Q.."]
]
題解
湊巧最近也要系統地學習回溯法了,我就把之前的這道題翻出來看了看
準備寫個題解
常規的 N皇后解題代碼會顯得更爲複雜
但在這道變式面前顯得不那麼必要,畢竟題目只是要求計算個數不是要求列舉所有解法
但實話實說自己當時根本沒有合適的思路,用N皇后問題的原解勉強莽了一個AC,在翻看評論區時發現了一個高點讚的代碼
實不相瞞我甚至看了兩遍都沒太看懂
直到用紙筆認認真真演算了一遍後才豁然開朗,心裏就一個詞——
牛逼!
只能說這種思維高度真的需要長時間的培養和一些突如其來的靈感的
今天我來重新爲這個代碼梳理一遍思路(我感覺和我一樣剛開始看得懵逼的人會很多)
實現代碼
class Solution {
public:
int solu = 0;
void dfs(int n, int row, int col, int m_dia, int a_dia) {
if(row == n) {
solu++;
return;
}
int free = ~(col | m_dia | a_dia) & ((1 << n) - 1);
while(free) {
int curr = free & -free;
dfs(n, row + 1, col | curr, (m_dia | curr) << 1, (a_dia | curr) >> 1);
free &= free - 1;
}
}
int totalNQueens(int n) {
dfs(n, 0, 0, 0, 0);
return solu;
}
};
解析
這個思路的核心解法在於DFS + 位運算剪枝
思路機制如下:
- 使用常規 DFS 層層搜索
- 用三個整型記錄每一層的哪個格子可以放置皇后,三個整型分別代表列,左斜下,右斜下 (col, m_dia, a_dia)
- 兩個核心位運算
x & -x 代表除最後一位 111 保留,其它位全部爲 000
x & (x - 1) 代表將最後一位 111 變成 000
函數:
int totalNQueens(int n) {
dfs(n, 0, 0, 0, 0);
return solu;
}
遞歸調用DFS
而函數DFS中:
語句:
int free = ~(col | m_dia | a_dia) & ((1 << n) - 1);
獲取當前行可放置的皇后位置
語句:
while(free) {
int curr = free & -free;
dfs(n, row + 1, col | curr, (m_dia | curr) << 1, (a_dia | curr) >> 1);
free &= free - 1;
}
獲取上一行的皇后約束條件
並通過位運算約束即將進行DFS的下一行
由此循環,直到滿足語句:
if(row == n) {
solu++;
return;
}
累加一次可行解,獲取遞歸出口
以上就是該N皇后變式問題的核心思路
位運算的細節我就不贅述了
個人覺得拿紙筆認真推演一遍應該是很好懂的