在下面的代码中,我试图通过回溯来解决N-Queen问题。我尝试过多次失败,但最后我还是吐出了一个解决方案。 这是我第一次使用这种方法(我第一次看到它),所以我想得到一些反馈。 我的算法提供的解决方案似乎是正确的,但我认为算法并不能正常工作,即使它 回溯。
以下是我遇到的几个问题:
failBoards
参数。我想用它作为避免重新获取之前失败的路径的手段。但是现在,该参数对算法没有影响,即可以省略。 (包含它是否有意义?如果是这样,我该如何使用它?)playedCols
参数应该确保每列只尝试一次。但是,当运行相同的列时,通常会尝试多次,而其他列则根本没有尝试过。playedCols.clear()
。我认为我真正应该做的就是恢复以前状态的playedCols
,但如果是这样的话,我该怎么做呢?这是代码。可以忽略Board类,我只是提供它以防有人想在自己的PC上运行它。
public class NQueens {
public static void main(String[] args) {
long start, end;
NQueens nq = new NQueens(8);
start = System.currentTimeMillis();
nq.placeQueens();
end = System.currentTimeMillis();
System.out.println("Time taken: " + (end-start) + "ms.");
// runs infinitely (sometimes causes a StackOverflowError) -> so it seems as if the solution is always correct
// do {
// nq.placeQueens();
// } while(!board.isCollision());
// System.out.println("here");
}
private static Board board; // for testing purposes (see main() above)
private final int SIZE;
private int iterations = 0;
public NQueens(final int N) {
SIZE = N;
}
public void placeQueens() {
List<Board> boards = new ArrayList<Board>();;
List<Board> failBoards = new ArrayList<Board>();
List<Integer> placedCols = new ArrayList<Integer>();
int queens = 0;
boards.add(new Board(SIZE));
placeQueens(boards, failBoards, placedCols, queens);
}
public void placeQueens(List<Board> boards, List<Board> failBoards, List<Integer> playedCols, int queens) {
iterations++;
Board temp = boards.get(boards.size()-1).copy();
// all queens placed
if(queens == SIZE) {
System.out.println(temp);
System.out.println("iterations: " + iterations);
board = temp.copy(); // for testing purposes (see main() above)
return;
}
// each row has one queen: "nth row <-> nth queen"
int row = queens;
// calculate a possible column, and place the queen on (row,column)
int col;
do {
col = new Random().nextInt(SIZE);
temp.addQueen(row, col, "" + queens);
} while(playedCols.contains(col) && playedCols.size() < SIZE && failBoards.contains(temp));
// the queen collides with another
if(temp.isCollision()) {
System.out.println("fail " + queens);
System.out.println(temp);
failBoards.add(temp);
// all possible columns on this row failed -> backtrack
if(playedCols.size() == SIZE) {
boards.remove(boards.size()-1);
// shouldn't I clear playedCols at this point? (Doing so often results in a StackOverflowError)
// playedCols.clear();
--queens;
} else {
// still other columns that can be tried
playedCols.add(col);
}
} else {
System.out.println("success " + queens);
System.out.println(temp);
// this placement seems to work: save the board and go for the next queen
boards.add(temp);
playedCols.clear();
++queens;
}
// recurse
placeQueens(boards, failBoards, playedCols, queens);
}
}
public class Board {
private String[][] board;
private final int SIZE;
private static final String EMPTY = "X";
public Board(final int N) {
board = new String[N][N];
SIZE = N;
init(board);
}
private static void init(String[][] board) {
for(int i=0; i < board.length; ++i) {
Arrays.fill(board[i], EMPTY);
}
}
public void addQueen(int row, int col, String val) {
board[row][col] = val;
}
public boolean isCollision() {
return isTwoOnARow() || isTwoOnACol() || isTwoOnDiagonal();
}
private boolean isTwoOnARow() {
int countOnRow = 0;
for(int i=0; i < board.length; ++i) {
for(int j=0; j < board[i].length; ++j) {
if(!board[i][j].equals(EMPTY)) {
countOnRow++;
}
}
if(countOnRow > 1) {
return true;
} else {
countOnRow = 0;
}
}
return false;
}
private boolean isTwoOnACol() {
int countOnCol = 0;
for(int i=0; i < board.length; ++i) {
for(int j=0; j < board[i].length; ++j) {
if(!board[j][i].equals(EMPTY)) {
countOnCol++;
}
}
if(countOnCol > 1) {
return true;
} else {
countOnCol = 0;
}
}
return false;
}
private boolean isTwoOnDiagonal() {
int countOnDiag = 0;
// bottom left to top right
int numDiagonals = board.length * 2;
for(int k=0; k < numDiagonals; ++k) {
for(int j=0; j <= k; ++j) {
int i = k - j;
if(i < board.length && j < board.length) {
if(!board[i][j].equals(EMPTY)) {
countOnDiag++;
}
}
}
if(countOnDiag > 1) {
return true;
} else {
countOnDiag = 0;
}
}
countOnDiag = 0;
// bottom right to top left
for (int i = board.length-1; i > 0; --i) {
for (int j = 0, k = i; k <= board.length - 1; j++, k++) {
if(!board[k][j].equals(EMPTY)) {
countOnDiag++;
}
}
if(countOnDiag > 1) {
return true;
} else {
countOnDiag = 0;
}
}
for (int i = 0; i <= board.length - 1; i++) {
for (int j = 0, k = i; k <= board.length - 1; j++, k++) {
if(!board[j][k].equals(EMPTY)) {
countOnDiag++;
}
}
if(countOnDiag > 1) {
return true;
} else {
countOnDiag = 0;
}
}
return false;
}
public String[][] getBoard() {
return this.board;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for(int i=0; i < board.length; ++i) {
sb.append("|");
for(int j=0; j < board[i].length; ++j) {
sb.append(board[i][j]);
sb.append("|");
}
// sb.append(System.lineSeparator());
// sb.append("-----------------");
sb.append(System.lineSeparator());
}
return sb.toString();
}
public Board copy() {
Board copy = new Board(SIZE);
for(int i=0; i < board.length; ++i) {
for(int j=0; j < board[i].length; ++j) {
copy.board[i][j] = board[i][j];
}
}
return copy;
}
}
这是5个皇后的一个输出。正如你所看到的,当试图在第二排(#1)找到女王的位置时,它......
...尝试6次,即使它最多需要4次尝试(这是由于#1)
成功0
| X | X | 0 | X | X |
| X | X | X | X | X |
| X | X | X | X | X |
| X | X | X | X | X |
| X | X | X | X | X |
失败1
| X | X | 0 | X | X |
| X | X | 1 | X | X |
| X | X | X | X | X |
| X | X | X | X | X |
| X | X | X | X | X |
失败1
| X | X | 0 | X | X |
| X | 1 | X | X | X |
| X | X | X | X | X |
| X | X | X | X | X |
| X | X | X | X | X |
失败1
| X | X | 0 | X | X |
| X | X | 1 | X | X |
| X | X | X | X | X |
| X | X | X | X | X |
| X | X | X | X | X |
失败1
| X | X | 0 | X | X |
| X | 1 | X | X | X |
| X | X | X | X | X |
| X | X | X | X | X |
| X | X | X | X | X |
失败1
| X | X | 0 | X | X |
| X | 1 | X | X | X |
| X | X | X | X | X |
| X | X | X | X | X |
| X | X | X | X | X |
| X | X | 0 | X | X |
| 1 | X | X | X | X |
| X | X | X | 2 | X |
| X | 3 | X | X | X |
| X | X | X | X | 4 |
迭代:11
所用时间:5ms。