N-Queens

描述

The n-queens puzzle is the problem of placing n queens on an n × n chessboard such that no two queens attack each other.

Eight Queens

Figure: Eight Queens

Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space respectively.

For example,There exist two distinct solutions to the 4-queens puzzle:

  1. [
  2. [".Q..", // Solution 1
  3. "...Q",
  4. "Q...",
  5. "..Q."],
  6. ["..Q.", // Solution 2
  7. "Q...",
  8. "...Q",
  9. ".Q.."]
  10. ]

分析

经典的深搜题。

设置一个数组 vector<int> C(n, 0), C[i] 表示第i行皇后所在的列编号,即在位置 (i, C[i])上放了一个皇后,这样用一个一维数组,就能记录整个棋盘。

代码1

  1. // N-Queens
  2. // 深搜+剪枝
  3. // 时间复杂度O(n!*n),空间复杂度O(n)
  4. class Solution {
  5. public:
  6. vector<vector<string> > solveNQueens(int n) {
  7. vector<vector<string> > result;
  8. vector<int> C(n, -1); // C[i]表示第i行皇后所在的列编号
  9. dfs(C, result, 0);
  10. return result;
  11. }
  12. private:
  13. void dfs(vector<int> &C, vector<vector<string> > &result, int row) {
  14. const int N = C.size();
  15. if (row == N) { // 终止条件,也是收敛条件,意味着找到了一个可行解
  16. vector<string> solution;
  17. for (int i = 0; i < N; ++i) {
  18. string s(N, '.');
  19. for (int j = 0; j < N; ++j) {
  20. if (j == C[i]) s[j] = 'Q';
  21. }
  22. solution.push_back(s);
  23. }
  24. result.push_back(solution);
  25. return;
  26. }
  27. for (int j = 0; j < N; ++j) { // 扩展状态,一列一列的试
  28. const bool ok = isValid(C, row, j);
  29. if (!ok) continue; // 剪枝,如果非法,继续尝试下一列
  30. // 执行扩展动作
  31. C[row] = j;
  32. dfs(C, result, row + 1);
  33. // 撤销动作
  34. // C[row] = -1;
  35. }
  36. }
  37. /**
  38. * 能否在 (row, col) 位置放一个皇后.
  39. *
  40. * @param C 棋局
  41. * @param row 当前正在处理的行,前面的行都已经放了皇后了
  42. * @param col 当前列
  43. * @return 能否放一个皇后
  44. */
  45. bool isValid(const vector<int> &C, int row, int col) {
  46. for (int i = 0; i < row; ++i) {
  47. // 在同一列
  48. if (C[i] == col) return false;
  49. // 在同一对角线上
  50. if (abs(i - row) == abs(C[i] - col)) return false;
  51. }
  52. return true;
  53. }
  54. };

代码2

  1. // N-Queens
  2. // 深搜+剪枝
  3. // 时间复杂度O(n!),空间复杂度O(n)
  4. class Solution {
  5. public:
  6. vector<vector<string> > solveNQueens(int n) {
  7. this->columns = vector<bool>(n, false);
  8. this->main_diag = vector<bool>(2 * n - 1, false);
  9. this->anti_diag = vector<bool>(2 * n - 1, false);
  10. vector<vector<string> > result;
  11. vector<int> C(n, -1); // C[i]表示第i行皇后所在的列编号
  12. dfs(C, result, 0);
  13. return result;
  14. }
  15. private:
  16. // 这三个变量用于剪枝
  17. vector<bool> columns; // 表示已经放置的皇后占据了哪些列
  18. vector<bool> main_diag; // 占据了哪些主对角线
  19. vector<bool> anti_diag; // 占据了哪些副对角线
  20. void dfs(vector<int> &C, vector<vector<string> > &result, int row) {
  21. const int N = C.size();
  22. if (row == N) { // 终止条件,也是收敛条件,意味着找到了一个可行解
  23. vector<string> solution;
  24. for (int i = 0; i < N; ++i) {
  25. string s(N, '.');
  26. for (int j = 0; j < N; ++j) {
  27. if (j == C[i]) s[j] = 'Q';
  28. }
  29. solution.push_back(s);
  30. }
  31. result.push_back(solution);
  32. return;
  33. }
  34. for (int j = 0; j < N; ++j) { // 扩展状态,一列一列的试
  35. const bool ok = !columns[j] && !main_diag[row - j + N - 1] &&
  36. !anti_diag[row + j];
  37. if (!ok) continue; // 剪枝,如果非法,继续尝试下一列
  38. // 执行扩展动作
  39. C[row] = j;
  40. columns[j] = main_diag[row - j + N - 1] = anti_diag[row + j] = true;
  41. dfs(C, result, row + 1);
  42. // 撤销动作
  43. // C[row] = -1;
  44. columns[j] = main_diag[row - j + N - 1] = anti_diag[row + j] = false;
  45. }
  46. }
  47. };

相关题目

原文: https://soulmachine.gitbooks.io/algorithm-essentials/content/cpp/dfs/n-queens.html