N-Queen Backtracking

时间:2016-09-27 23:20:00

标签: java algorithm backtracking n-queens

在下面的代码中,我试图通过回溯来解决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)找到女王的位置时,它......

  1. ...试图将王后放在以前尝过的专栏上
  2. ...尝试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 |

    成功1
    | X | X | 0 | X | X |
    | 1 | X | X | X | X |
    | X | X | X | X | X |
    | X | X | X | X | X |
    | X | X | X | X | X |

    成功2
    | X | X | 0 | X | X |
    | 1 | X | X | X | X |
    | X | X | X | 2 | X |
    | X | X | X | X | X |
    | X | X | X | X | X |

    成功3
    | X | X | 0 | X | X |
    | 1 | X | X | X | X |
    | X | X | X | 2 | X |
    | X | 3 | X | X | X |
    | X | X | X | X | X |

    成功4
    | 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 |

    | 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。

0 个答案:

没有答案