51. N 皇后

image.png
image.png

N皇后是经典的回溯问题,给定一个N,在N × N的棋盘上放置N个皇后,使每个皇后之间不能攻击(任何两个皇后不能在同一行、同一列或者一条斜线上)

思路一:直接回溯

完整代码:

  1. class Solution {
  2. public:
  3. vector<vector<string>> result;
  4. // n 为输入的棋盘大小
  5. // row 是当前递归到棋牌的第几行了
  6. void backtracking(int n, int row, vector<string>& chessboard) {
  7. if (row == n) {
  8. result.push_back(chessboard);
  9. return;
  10. }
  11. for (int col = 0; col < n; col++) {
  12. if (isValid(row, col, chessboard, n)) {//可以放
  13. chessboard[row][col] = 'Q'; // 放置皇后
  14. backtracking(n, row + 1, chessboard);
  15. chessboard[row][col] = '.'; // 回溯,撤销皇后
  16. }
  17. }
  18. }
  19. bool isValid(int row, int col, vector<string>& chessboard, int n) {
  20. // 检查列
  21. for (int i = 0; i < row; i++) {
  22. if (chessboard[i][col] == 'Q') return false;
  23. }
  24. // 检查45度
  25. for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
  26. if (chessboard[i][j] == 'Q') return false;
  27. }
  28. // 检查135度
  29. for (int i = row -1, j = col + 1; i >=0 && j < n; i--, j++) {
  30. if (chessboard[i][j] == 'Q') return false;
  31. }
  32. return true;
  33. }
  34. vector<vector<string>> solveNQueens(int n) {
  35. vector<string> chessboard(n, string(n, '.'));
  36. backtracking(n, 0, chessboard);
  37. return result;
  38. }
  39. };

思路二:利用组合数

如果把皇后的位置看作是坐标[i, j],那么生成从1, n的一个排列,得到排列数组,下标记为行数,对应的值记为列数,那么用这个排列就可以确定一个棋盘,并且每个皇后的行数和列数必不相同,因此只需通过判断两个皇后是否处于同一个斜线上,来去掉一些棋盘,就可以得到最终解

完整代码:

class Solution {
public:
    vector<vector<string>> result;
    vector<int> path;

    void backtracking(int n, int row, vector<string>& chessboard, vector<bool>& used) {
        if (row == n) {
            bool flag = true;
            for (int i = 0; i < n; i++) {
                for (int j = i + 1; j < n; j++) {
                    if (abs(i - j) == abs(path[i] - path[j])) // 在一对角线上
                        flag = false;
                }
            }
            if (flag)
                result.push_back(chessboard);
            return;
        }
        for (int i = 0; i < n; i++) {
            if (!used[i]) {
                path.push_back(i);
                chessboard[row][i] = 'Q';
                used[i] = true;
                backtracking(n, row + 1, chessboard, used);
                path.pop_back();
                chessboard[row][i] = '.';
                used[i] = false;
            }
        }

    }

    vector<vector<string>> solveNQueens(int n) {
        vector<string> chessboard(n, string(n, '.'));
        vector<bool> used(n, false);
        backtracking(n, 0, chessboard, used);
        return result;
    }
};

剪枝

在生成棋盘的过程中进行判断,如果前面存在处于统一斜线的皇后,直接终止

class Solution {
public:
    vector<vector<string>> res;
    vector<int> p; // 生成的排列
    void dfs(vector<string>& c, int start, vector<bool>& used) {
        if (start == c.size()) {
            res.push_back(c);
            return;
        }
        for (int i = 0; i < c.size(); ++i) {
            if (!used[i]) {
                bool flag = true;
                for (int j = 0; j < p.size(); ++j) { // 判断之前的queen是否和当前要放置的处于同一对角线
                    if (abs(start - j) == abs(i - p[j])) { // start是当前queen的横坐标,i是纵坐标
                        flag = false;
                        break;
                    }
                }
                if (flag) {
                    c[start][i] = 'Q';
                    p.push_back(i);
                    used[i] = true;
                    dfs(c, start + 1, used);
                    used[i] = false;
                    p.pop_back();
                    c[start][i] = '.';
                }

            }
        }
    }
    vector<vector<string>> solveNQueens(int n) {
        vector<bool> used(n, false);
        vector<string> c(n, string(n, '.'));
        dfs(c, 0, used);
        return res;
    }
};

image.png可以看到,剪枝不剪枝差别还是很大的