51. N 皇后

51. N 皇后 - 力扣(LeetCode)

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,指的是「所有可能的解」。