介紹
|
在西洋棋當中的棋盤中,「皇后」能攻擊的方向,是自己所在位置的十字(上下左右),以及斜對角的所有方向。現在給定 的棋盤,問在這個棋盤當中,放下 個棋子,並且不能互相攻擊到彼此,一共有幾種擺法。 |
|
數組做法
定義dfs(i)代表從第i列擺放的方法數,每列從 中選個位置擺放,並檢查跟之前的擺放有無衝突,如果走到底,代表找到了一種擺放方式。
board[i]代表棋盤當中第i列當前擺放皇后的位置,也就避免了同一列擺兩個皇后的衝突情形。- 判斷斜對角的方式是斜率,假如斜率為1或-1,兩個點就在棋盤的同一個位置。
下圖演示了,在 的棋盤中,從第 0 列開始嘗試的部分過程

程式碼:N皇后,數組方法(不推薦)
class Solution {
public:
int totalNQueens(int n) {
vector<int> board(n); // 每一列放置的位置
auto check = [&](int i, int j) -> bool {
for(int k = 0; k < i; ++k) {
if(j == board[k] || abs(i - k) == abs(board[k] - j)) { // 前面放到一樣的位置 or dx == dy (對角線有撞到), 就不能放
return false;
}
}
return true;
};
auto dfs = [&](this auto&&dfs, int i) {
if(i == n) { // 到達結尾, 代表找到一種正確方式
return 1;
}
int res = 0;
for(int j = 0; j < n; ++j) { // 每個位置都放放看
if(check(i, j)) { // 可以放置
board[i] = j;
res += dfs(i + 1); // 下一列
}
}
return res;
};
return dfs(0);
}
};
位運算方法
列限制
|
假如棋盤的某個位置擺放了皇后,就將對應的位置標為 ,代表此位置使用過。 |
|
主對角線限制
|
在第一個皇后擺放之後,要怎麼表示「主對角線」在下一列的限制?
|
|
副對角線限制
|
副對角線,跟主對角線同理。
|
|
程式碼:N皇后,位運算作法
class Solution {
private:
int dfs(int& limit, int col, int right, int left) { // right 主對角線, left 副對角線
if(col == limit) { // 所有位置都被選過了, 找到一個合法路徑
return 1;
}
int ban = col | right | left;
int candidate = limit & (~ban); // 可以選的位置有哪些
int place = 0;
int res = 0;
while(candidate != 0) {
place = candidate & (-candidate); // 取出最右的 1
candidate ^= place; // 把最右邊的 1 消除掉。
res += dfs(limit, col | place, limit & ((right | place) << 1), (left | place) >> 1);
}
return res;
}
public:
int totalNQueens(int n) {
int limit = (1 << n) - 1; // 限制範圍, 0...接 n 個 1
return dfs(limit, 0, 0, 0);
}
};
複雜度分析
- 時間複雜度:
- 空間複雜度:
攻擊方向

