所以我正在努力解决n-queens问题。我想我有一个有效的回溯实现,但我认为我检查一块板是否有效的方法是关闭的(以及效率极低),但我不明白为什么。任何人都可以看到为什么/提供更好的方法?
/** Given an N x N chess board, find placements for N queens on the board such that
* none of them are attacking another queen (two queens are attacking each other if
* they occupy the same row, column, or diagonal).
* Print out the row, col coordinates for each queen on a separate line, where the
* row and column numbers are separated by a single space. */
static void solveQueens(int n) {
boolean[][] board = new boolean[n][n];
board = solveQueens(board, n);
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board.length; j++) {
if (board[i][j]) {
System.out.printf("%s %s\n", i, j);
}
}
}
}
/** Returns a solved board for the n-queens problem, given an empty board. */
static boolean[][] solveQueens(boolean[][] board, int queensLeft) {
if (queensLeft == 0) {
return board;
}
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board.length; j++) {
if (board[i][j]) { continue; }
board[i][j] = true;
if (isValid(board)) {
return solveQueens(board, queensLeft - 1);
} else {
board[i][j] = false;
}
}
}
return null;
}
/** True iff BOARD is a valid solution, with no queens attacking each other. */
static boolean isValid(boolean[][] board) {
boolean occupied = false;
//Horizontal
for (int i = 0; i < board.length; i++) {
for (boolean queen : board[i]) {
if (queen && occupied) {
return false;
}
if (queen) {
occupied = true;
}
}
occupied = false;
}
//Vertical
for (int i = 0; i < board.length; i++) {
for (int j = board.length - 1; j >= 0; j--) {
boolean queen = board[j][i];
if (queen && occupied) {
return false;
}
if (queen) {
occupied = true;
}
}
occupied = false;
}
//Diagonals
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board.length; j++) {
if (board[i][j]) {
for (int k = 0; k < board.length; k++) {
for (int l = 0; l < board.length; l++) {
if (((i - k) == (j - l)) && board[k][l] && !(k == i && l == j)) {
return false;
}
}
}
}
}
}
return true;
}
答案 0 :(得分:1)
您可能希望修改第二个2^(n*n)
函数,尝试为每一行放置最多一个女王,而不是试图为每个正方形放置一个女王(效率非常低solveQueens
)。换句话说,较长的solveQueens
函数将尝试每行的每个可能的列,并继续前进到下一行。
另一点是,第二个board
函数的solveQueens
变量已经就地修改了,所以我们实际上不必返回它。相反,我们只需返回true
或false
值即可指示是否存在解决方案。
因此第一个solveQueens
函数可以更改为:
static void solveQueens(int n) {
boolean[][] board = new boolean[n][n];
// boolean return value from second solveQueens function
boolean solved = solveQueens(board, n);
if (solved) {
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board.length; j++) {
if (board[i][j]) {
System.out.printf("%s %s\n", i, j);
}
}
} else {
System.out.printf("No solution for board of size %d\n", n);
}
}
第二个修改后的solveQueens
函数,递归地向下移动每一行,并为每一行尝试所有可能的列:
static boolean solveQueens(boolean[][] board, int row, int n) {
// we have a queen for each row, done
if (row >= n) {
return board;
}
// for the current row, try placing a queen at column 0
// if that fails, try placing a queen at column 1
// if that fails, try placing a queen at column 2, and so on
for (int j = 0; j < board.length; j++) {
board[row][j] = true;
if (isValid(board)) {
// placing at (row, j) is valid, try next row
boolean boardSolved = solveQueens(board, row + 1, n);
if (boardSolved) {
// board is solved, yay
return boardSolved;
}
}
// cannot place queen at (row, j) with current board configuration.
// set board[row][j] back to false
board[i][j] = false;
}
// tried every column in current row and still cant solve, return false
return false;
}
对于isValid
函数的这部分:
//Diagonals
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board.length; j++) {
if (board[i][j]) {
for (int k = 0; k < board.length; k++) {
for (int l = 0; l < board.length; l++) {
if (((i - k) == (j - l)) && board[k][l] && !(k == i && l == j)) {
return false;
}
}
}
}
}
}
return true;
在最里面的if
,您必须使用(abs(i - k) == abs(j - l))
而不是(i - k) == (j - l)
。原始代码失败的示例是i = 0
,j = 3
,k = 3
,l = 0
(一个女王在第0行第3列,第二个女王在行上) 3列0),所以(i - k) == 0 - 3 == -3
和(j - l) == 3 - 0 == 3
,即使这两个皇后彼此对角,最里面的if
也无法检测到它。使用abs(i - k) == abs(j - l)
会将行距离(i - k
)和列距离(j - l
)转换为绝对值,因此可以正常工作。
所以只需改变这一行:
if (((i - k) == (j - l)) && board[k][l] && !(k == i && l == j)) {
为:
if ((abs(i - k) == abs(j - l)) && board[k][l] && !(k == i && l == j)) {
并且isValid
函数应该没问题。
希望有所帮助!