我是递归和回溯的新手。我正在尝试解决N-Queen问题,以打印所有解决方案,而不是仅打印1个,并理解这些概念。
我认为我得到了一些解决方案后却部分实现了算法,但并非全部打印出来。我的代码使用Java。
我无法弄清楚自己在犯什么错误。我的想法是意识到第一个皇后必须排在第一行,第二个皇后排在第二行,依此类推。我必须弄清楚哪一列是合适的,显然要注意用对角线放置一个皇后。
感谢您为我指明正确方向的任何帮助。我的代码在下面,我尝试添加注释,以帮助理解。
public class nQueens {
static class Queen {
public Queen( int row, int column) {
this.row = row;
this.column = column;
}
int row = -1;
int column = -1;
}
static ArrayList<Queen> queens = new ArrayList<Queen>();
public static void main(String argv[]) {
int n = 5;
int[][] chessBoard = new int[n][n];
int placed = 0;
solve(n, chessBoard, placed);
}
public static void solve(int n, int[][] chessBoard, int placed) {
// this means that all the queens have been placed
if (placed == n) {
System.out.println("**** Solution *****");
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
System.out.print(chessBoard[i][j] + " ");
}
System.out.println();
}
} else {
// we know that each queen can be placed on each row
int i = placed;
// iterate through the columns
for (int j = 0; j < n; j++) {
if (chessBoard[i][j] != 1) {
if (isSafe(i, j)) {
chessBoard[i][j] = 1;
Queen queen = new Queen( i, j);
queens.add(queen);
placed = placed + 1;
// solve for the remaining number of queens
solve(n, chessBoard, placed);
// un-mark the spot
chessBoard[i][j] = 0;
// remove the placed queen
queens.remove(queens.size() - 1);
placed = placed - 1;
}
}
}
}
}
public static boolean isSafe(int row, int column) {
// this means that there are no queens on the board
if (queens.isEmpty()) {
return true;
} else {
for (int i = 0; i < queens.size(); i++) {
// same column
if (queens.get(i).column == column) {
return false;
}
// check diagonal
int slope = Math.abs((queens.get(i).row - row) / (queens.get(i).column - column));
if (slope == 1) {
return false;
}
}
}
return true;
}
}
答案 0 :(得分:1)
问题是:
int slope = Math.abs((queens.get(i).row - row) / (queens.get(i).column - column));
if (slope == 1) {
return false;
}
您正在将slope
转换为整数。这意味着1.5
或1.3
的斜率变为1
,即使您实际上不在那个对角线上也导致您返回false
。
而不是在除法之前强制转换为浮点数(请注意,java的除法是整数除法,因此您需要先将除数或除数强制转换为浮点数才能获得浮点输出)以允许浮点斜率:
float tmp = (queens.get(i).row - row);
float slope = Math.abs(tmp/ (queens.get(i).column - column));
if (slope == 1) {
return false;
}
答案 1 :(得分:1)
isSafe()
和class Queen
的替代解决方案
以下是在placer
闭包中传递的通用求解器。通过使用这种方法,可以简单地将相同的求解器用于白嘴鸦(placeRook()
),骑士(placeKnight()
)或主教(placeBishop()
)。
请注意,我的解决方案是用Groovy编写的,它也可以在JVM上运行,并且非常接近Java。因此,将算法的多汁部分转换为Java应该没问题。
class ChessBoard {
int N
int lastIndex
private boolean[][] board
int solutions
ChessBoard(int n) {
board = new boolean[n][n]
N = n
lastIndex = n - 1
solutions = 0
this.each { int row, int column -> board[row][column] = true }
}
ChessBoard(ChessBoard orig) {
N = orig.getN()
board = new boolean[N][N]
lastIndex = N - 1
solutions = 0
this.each { int row, int column -> board[row][column] = orig.getField(row, column) }
}
void each(Closure c) {
(0..lastIndex).each { row ->
(0..lastIndex).each { column -> c(row, column) }
}
}
void print() {
println " ${'-' * N}"
(0..lastIndex).each { row ->
print "|"
(0..lastIndex).each { column -> print "${board[row][column] ? ' ' : 'X'}" }
println "|"
}
println " ${'-' * N}"
}
int getN() { return N }
int getSolutions() { return solutions }
boolean getField(int row, int column) { return board[row][column] }
void blockField(int row, int column) {
if ((row < 0) || (row > lastIndex))
return
if ((column < 0) || (column > lastIndex))
return
board[row][column] = false
}
List<Integer> getFree(int row) {
(0..lastIndex).findResults { int column -> board[row][column] ? column : null }
}
void placeQueen(int row, int column, boolean all = true) {
if (all) {
(0..lastIndex).each { offset ->
blockField(row, offset) // row
blockField(offset, column) // column
blockField(row + offset, column + offset) // diagonals
blockField(row + offset, column - offset)
blockField(row - offset, column + offset)
blockField(row - offset, column - offset)
}
} else {
blockField(row, column)
}
}
// recursive solver
void solve(ChessBoard previous, List<Integer> columns, int row, Closure placer) {
List<Integer> free = previous.getFree(row)
if (row < lastIndex) {
// recurse
free.each { column ->
ChessBoard work = new ChessBoard(previous)
columns[row] = column
placer(work, row, column, true)
solve(work, columns, row + 1, placer)
}
} else {
// solutions
free.each { column ->
ChessBoard solution = new ChessBoard(N)
columns[row] = column
(0..lastIndex).each { placer(solution, it, columns[it], false) }
println "Solution #${++solutions}:"
solution.print()
}
}
}
// start recursion
void solve(Closure placer) {
List<Integer> columns = []
solve(this, columns, 0, placer)
}
}
board = new ChessBoard(8)
board.solve { ChessBoard work, int row, int column, boolean all -> work.placeQueen(row, column, all) }
println "Solutions: ${board.getSolutions()}"
试运行:
Solution #1:
--------
|X |
| X |
| X|
| X |
| X |
| X |
| X |
| X |
--------
...
Solution #92:
--------
| X|
| X |
|X |
| X |
| X |
| X |
| X |
| X |
--------
Solutions: 92
如果我的记忆正确地为我服务,则92听起来确实可以解决8皇后问题。但是自从我在学校使用Pascal的迭代方法解决此问题以来,已有35年了:-)
更新改进的解决方案
ChessBoard
(用于跟踪状态)和Solver
(用于算法)class ChessBoard {
private int N
private int lastIndex
private boolean[][] state
ChessBoard(int n) {
N = n
lastIndex = N - 1
state = new boolean[N][N]
(0..lastIndex).each { row ->
(0..lastIndex).each { column ->
setField(row, column, true)
}
}
}
ChessBoard(ChessBoard orig) {
N = orig.getN()
lastIndex = N - 1
state = new boolean[N][N]
(0..lastIndex).each { row ->
(0..lastIndex).each { column ->
setField(row, column, orig.getField(row, column))
}
}
}
int getN() {
return N
}
boolean getField(int row, int column) {
return state[row][column]
}
void setField(int row, int column, boolean free = false) {
if ((row < 0) || (row > lastIndex))
return
if ((column < 0) || (column > lastIndex))
return
state[row][column] = free
}
List<Integer> getFree(int row) {
(0..lastIndex)
.findResults { int column ->
getField(row, column) ? column : null
}
}
// for debugging only
void print() {
println " ${'-' * N}"
(0..lastIndex).each { row ->
print "|"
(0..lastIndex).each { column -> print "${getField(row, column) ? ' ' : 'X'}" }
println "|"
}
println " ${'-' * N}"
}
}
class Solver {
private int N
private int lastIndex
private int solutions
private int[] columns
Solver(int n) {
N = n
lastIndex = N - 1
solutions = 0
columns = new int[N]
}
void printSolution(String label) {
solutions++
if (!label)
return
println "${N}-${label} solution #${solutions}"
println " ${'-' * N}"
(0..lastIndex).each { row ->
int column = columns[row]
println "|${' ' * column}X${' ' * (lastIndex - column)}|"
}
println " ${'-' * N}"
}
int getSolutions() {
return solutions
}
void placeQueen(ChessBoard board, int row, int column) {
// only modify fields from (row+1) downwards
(1..(lastIndex - row)).each { offset ->
board.setField(row + offset, column) // column
board.setField(row + offset, column + offset) // diagonals
board.setField(row + offset, column - offset)
}
}
void placeRook(ChessBoard board, int row, int column) {
// only modify fields from (row+1) downwards
(1..(lastIndex - row)).each { offset ->
board.setField(row + offset, column) // column
}
}
void placeBishop(ChessBoard board, int row, int column) {
// only modify fields from (row+1) downwards
(1..(lastIndex - row)).each { offset ->
board.setField(row + offset, column + offset) // diagonals
board.setField(row + offset, column - offset)
}
}
static void placeKnight(ChessBoard board, int row, int column) {
// only modify fields from (row+1) downwards
board.setField(row + 1, column - 2)
board.setField(row + 1, column + 2)
board.setField(row + 2, column - 1)
board.setField(row + 2, column + 1)
}
// recursive solver
void solve(ChessBoard previous, int row, Closure placer, String label) {
List<Integer> free = previous.getFree(row)
if (row < lastIndex) {
// recurse
free.each { column ->
ChessBoard work = new ChessBoard(previous)
columns[row] = column
placer(this, work, row, column)
solve(work, row + 1, placer, label)
}
} else {
// solutions
free.each { column ->
columns[row] = column
printSolution(label)
}
}
}
// start recursion
int solve(Closure placer, String label = null) {
solve(new ChessBoard(N), 0, placer, label)
return solutions
}
}
Map<String, Closure> placers = [
'Queens': { Solver solver, ChessBoard board, int row, int column -> solver.placeQueen(board, row, column) },
'Rooks': { Solver solver, ChessBoard board, int row, int column -> solver.placeRook(board, row, column) },
'Bishops': { Solver solver, ChessBoard board, int row, int column -> solver.placeBishop(board, row, column) },
'Knights': { Solver solver, ChessBoard board, int row, int column -> solver.placeKnight(board, row, column) },
]
Map<String, List<Integer>> solutions = [:]
// generate solutions up to maxN
int maxN = 8
boolean print = false
placers
.keySet()
.each { String key ->
Closure placer = placers[key]
List<Integer> results = []
(1..maxN).each { N ->
results.push(new Solver(N).solve(placer, print ? key : null))
}
solutions[key] = results
}
// generate markdown table from solutions
List lines = []
(0..maxN).each { lines[it] = [it ?: 'Size'] }
[
'Queens',
'Rooks',
'Bishops',
'Knights',
].each { key ->
List<Integer> results = solutions[key]
lines[0].push(key)
(1..maxN).each { lines[it].push(results[it - 1]) }
}
lines.each { line -> println line.join('|') }
return
结果表:
| Size | Queens | Rooks | Bishops | Knights |
|------|--------|-------|---------|---------|
| 1 | 1 | 1 | 1 | 1 |
| 2 | 0 | 2 | 2 | 4 |
| 3 | 0 | 6 | 5 | 9 |
| 4 | 2 | 24 | 24 | 52 |
| 5 | 10 | 120 | 125 | 451 |
| 6 | 4 | 720 | 796 | 4898 |
| 7 | 40 | 5040 | 5635 | 67381 |
| 8 | 92 | 40320 | 48042 | 1131382 |
|------|--------|-------|---------|---------|