51. N 皇后
class Solution {
// This should be spoken as `vector<vector<array<char, n>>>`.
vector<vector<string>> Result_;
vector<int> Col_, Diag_, Back_;
vector<string> Path_;
void DFS(int R, int N) {
if (R == N) {
Result_.push_back(Path_);
return;
}
// Iterate over the column.
for (int C = 0; C < N; ++C) {
// If the col/diag/back is marked, skip.
if (Col_[C] || Diag_[N + C - R] || Back_[C + R])
continue;
// Generate string of this row.
string Current_(N, '.');
Current_[C] = 'Q';
// Mark col, diag, and backs; then move to next row.
Col_[C] = Diag_[N + C - R] = Back_[C + R] = 1;
Path_.push_back(Current_);
DFS(R + 1, N);
Path_.pop_back();
Col_[C] = Diag_[N + C - R] = Back_[C + R] = 0;
}
}
public:
vector<vector<string>> solveNQueens(int n) {
Col_.resize(n);
// To avoid negative indices, we have to make their size twice of n.
Diag_.resize(2 * n);
Back_.resize(2 * n);
DFS(0, n);
return Result_;
}
};
也是经典问题了。这个题目的关键点在于对标记数组的设计。标记列数组非常容易;而对于对角线数组,经过数学推断可以发现,正对角线的下标差相等,反对角线的下标和相等,于是可以借助另外两个数组完整地表达所有对角线。
但是要注意,对角线空间的大小是行列空间的 2 倍,所以我们的 Diag_ 和 Back_ 数组需要设置为 \(2n\) 大小。
然后,我们知道行上、列上和两条对角线上都不能放置皇后,而行已经由深搜策略确保不会多次访问,所以只需要关注列和对角线。于是就有了 Col_[C] || Diag_[N + C - R] || Back_[C + R] 这么长一串判断条件。
最后注意搞明白这道题的层次。题解的返回值 vector<vector<string>> 的意思是:每一行用一个 string,也即 array<char, n> 来表示,形成的 vector<string> 是一张完整的棋盘。外面又加了一层 vector,指的是「所有可能的解」。