八皇后问题是一个以国际象棋为背景的问题:如何能够在8 × 8 的国际象棋棋盘上放置八个皇后(Queen),使得任何一个皇后都无法直接吃掉其他的皇后。为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的N皇后摆放问题:这时棋盘的大小变为 N ×N,而皇后个数也变成 N。当且仅当 n = 1 或 n ≥ 4 时问题有解。
生成棋盘所有皇后摆放的可能配置,并打印满足给定约束的配置。回溯算法的思想是将皇后从最左边的列开始一一放置在不同的列中。 当我们将皇后放在一列中时,检查是否与已经放置的皇后发生冲突。 在当前列中,如果找到没有冲突的行,则将该行和列标记为解决方案的一部分。 如果由于冲突而找不到这样的行,那么我们将回溯并返回 false。
首先是从棋盘最左边的列开始摆放皇后。
算法主要内容:
1. 当 col = N 时,所有皇后摆放完毕,问题有解
2. 算法从每一列的每一行来推进问题的求解:
3. 如果所有列所有行都尝试探索过后依然无解,返回 false(回溯)。
检查当前位置放置皇后会与之前放置的皇后冲突。这里的时间复杂度为 O(N)。
1 /** 2 * 用于检查是否可以将Queen放在当前位置上 3 * 4 * @param board 5 * @param row 6 * @param col 7 * @return 8 */ 9 private boolean isSafe(int[][] board, int row, int col) { 10 int i, j; 11 12 /* 是否在同一行(左侧) */ 13 for (i = 0; i < col; i++) 14 if (board[row][i] == 1) 15 return false; 16 17 /* 是否在反对角线方向上 */ 18 for (i = row, j = col; i >= 0 && j >= 0; i--, j--) 19 if (board[i][j] == 1) 20 return false; 21 22 /* 是否在对角线方向上 */ 23 for (i = row, j = col; j >= 0 && i < N; i++, j--) 24 if (board[i][j] == 1) 25 return false; 26 27 return true; 28 }
通过递归方式来探索问题的解,在所有位置尝试完之前,探索问题的解失败时通过回溯来寻找下一个皇后的摆放位置,回溯同时把摆放位置的标记复原。对于N皇后的时间复杂度,分析起来有点复杂,但从中可以分析其时间复杂度为 O(N3)。
1 /** 2 * 递归求解N-Queen问题 3 * 4 * @param board 5 * @param col 6 * @return 7 */ 8 private boolean solveNQUtils(int[][] board, int col) { 9 /* 如果所有皇后都被放置, 然后返回true */ 10 if (col >= N) 11 return true; 12 13 /* 考虑当前col能否摆放Queen */ 14 for (int i = 0; i < N; i++) { 15 /* 当前位置是否能摆放Queen */ 16 if (isSafe(board, i, col)) { 17 board[i][col] = 1; 18 19 /* 再次摆放Queen, 如果成功直接返回true */ 20 if (solveNQUtils(board, col + 1)) { 21 return true; 22 } 23 24 /* 这是在某列无法放Queen回溯时把queen移走 */ 25 board[i][col] = 0; 26 } 27 } 28 29 return false; 30 }
本文源代码:
1 package algorithm; 2 3 /** 4 * N皇后问题,回溯法,简单分析一下,时间复杂度大概为 O(N^3) 5 */ 6 public class NQueenProblem { 7 private final int N = 8; 8 9 /** 10 * 打印符合条件的摆放棋盘 11 * 12 * @param board 13 */ 14 private void printSolution(int[][] board) { 15 for (int i = 0; i < N; i++) { 16 for (int j = 0; j < N; j++) { 17 System.out.print(" " + board[i][j] + " "); 18 } 19 System.out.println(); 20 } 21 } 22 23 /** 24 * 用于检查是否可以将Queen放在当前位置上 25 * 26 * @param board 27 * @param row 28 * @param col 29 * @return 30 */ 31 private boolean isSafe(int[][] board, int row, int col) { 32 int i, j; 33 34 /* 是否在同一行(左侧) */ 35 for (i = 0; i < col; i++) 36 if (board[row][i] == 1) 37 return false; 38 39 /* 是否在反对角线方向上 */ 40 for (i = row, j = col; i >= 0 && j >= 0; i--, j--) 41 if (board[i][j] == 1) 42 return false; 43 44 /* 是否在对角线方向上 */ 45 for (i = row, j = col; j >= 0 && i < N; i++, j--) 46 if (board[i][j] == 1) 47 return false; 48 49 return true; 50 } 51 52 /** 53 * 递归求解N-Queen问题 54 * 55 * @param board 56 * @param col 57 * @return 58 */ 59 private boolean solveNQUtils(int[][] board, int col) { 60 /* 如果所有皇后都被放置, 然后返回true */ 61 if (col >= N) 62 return true; 63 64 /* 考虑当前col能否摆放Queen */ 65 for (int i = 0; i < N; i++) { 66 /* 当前位置是否能摆放Queen */ 67 if (isSafe(board, i, col)) { 68 board[i][col] = 1; 69 70 /* 再次摆放Queen, 如果成功直接返回true */ 71 if (solveNQUtils(board, col + 1)) { 72 return true; 73 } 74 75 /* 这是在某列无法放Queen回溯时把queen移走 */ 76 board[i][col] = 0; 77 } 78 } 79 80 return false; 81 } 82 83 /** 84 * 解决并打印N-Queen的问题 85 * 86 * @return 87 */ 88 private boolean solveNQ() { 89 int[][] board = new int[N][N]; 90 91 if (!solveNQUtils(board, 0)) { 92 System.out.println("Solution does not exist"); 93 return false; 94 } 95 96 printSolution(board); 97 return true; 98 } 99 100 public static void main(String[] args) { 101 NQueenProblem queen = new NQueenProblem(); 102 queen.solveNQ(); 103 } 104 }
原文:https://www.cnblogs.com/magic-sea/p/12093756.html