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

代码2

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

相关题目

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