八个皇后拼图逻辑错误

时间:2012-10-01 04:42:22

标签: java recursion backtracking

我正在编写一个程序来递归地解决八个女王之谜,但我很难绕过我应该如何让我的程序“回溯”来解决问题。我的Solve()方法和我的isOpen()方法可能都是错误的,但我无法弄清楚我做错了什么。我的代码如下(和驱动程序)。对不起它已经很久了,但如果我能得到一些提示,我会很感激。

注意*图像图标只是Freddie Mercury的图片

import java.io.*;
import java.util.Formatter;
import javax.swing.*;

import java.awt.*;

public class EightQueens extends JFrame {

// declare class constants as needed
final int BOARD_DIMENSION = 8;
final boolean[][] BOARD = new boolean[8][8];
// declare instance variables
PrintWriter output;
int backtrack;
int comparison;
File file;
// gui components
JPanel chessPanel;
JPanel square[];
Icon queenIcon;
JLabel imageLabel;

// constructor method
public EightQueens() throws FileNotFoundException {

    // gui stuff
    setTitle("8 Queens: Freddie Style");
    setPreferredSize(new Dimension(800, 800));
    chessPanel = new JPanel(new GridLayout(8, 8));
    chessPanel.setPreferredSize(new Dimension(100, 100));
    chessPanel.setBackground(Color.BLACK);
    square = new JPanel[64];
    queenIcon = new ImageIcon(getClass().getResource(
            "/resource/mercury.jpg"));
    JLabel imageLabel = new JLabel("", queenIcon, JLabel.CENTER);

    // CREATE AND INITIALIZE THE OUTPUT FILE
    file = new File("TraceQueens.txt");
    output = new PrintWriter(file);

    // CREATE AND INITIALIZE BOARD
    for (int i = 0; i < 8; i++)
        for (int j = 0; j < 8; j++)
            BOARD[i][j] = false;

    // add the squares to the chess panel
    for (int i = 0; i < square.length; i++)// adds chess squares to the
                                            // board
    {
        square[i] = new JPanel(new BorderLayout());
        square[i].setBackground(Color.BLACK);
        chessPanel.add(square[i]);
    }
    // this will make the squares the correct colors
    for (int i = 0; i < BOARD_DIMENSION; i = i + 2)
        square[i].setBackground(Color.WHITE);
    for (int i = BOARD_DIMENSION + 1; i < (2 * BOARD_DIMENSION)
            && i > BOARD_DIMENSION; i = i + 2)
        square[i].setBackground(Color.WHITE);
    for (int i = (2 * BOARD_DIMENSION); i < (3 * BOARD_DIMENSION)
            && i >= (2 * BOARD_DIMENSION); i = i + 2)
        square[i].setBackground(Color.WHITE);
    for (int i = (3 * BOARD_DIMENSION) + 1; i < (4 * BOARD_DIMENSION)
            && i > (3 * BOARD_DIMENSION); i = i + 2)
        square[i].setBackground(Color.WHITE);
    for (int i = (4 * BOARD_DIMENSION); i < (5 * BOARD_DIMENSION)
            && i >= (4 * BOARD_DIMENSION); i = i + 2)
        square[i].setBackground(Color.WHITE);
    for (int i = (5 * BOARD_DIMENSION) + 1; i < (6 * BOARD_DIMENSION)
            && i > (5 * BOARD_DIMENSION); i = i + 2)
        square[i].setBackground(Color.WHITE);
    for (int i = (6 * BOARD_DIMENSION); i < (7 * BOARD_DIMENSION)
            && i >= (6 * BOARD_DIMENSION); i = i + 2)
        square[i].setBackground(Color.WHITE);
    for (int i = (7 * BOARD_DIMENSION) + 1; i < (8 * BOARD_DIMENSION)
            && i > (7 * BOARD_DIMENSION); i = i + 2)
        square[i].setBackground(Color.WHITE);

    // puts the chess panel on the EightQueens frame
    add(chessPanel);

} // end constructor

// user interface method used to wrap the parameter representing the first
// column
public boolean solve() {
    return solve(0);
}

// recursive method solve

public boolean solve(int column) {

    // The Queen has been placed on the board in column
    int row = 0; 
    BOARD[row][column] = true;
    output.println("The queen has been placed at [0][" + column + "]");

    // if column is beyond the last column, then the problem is solved
    // base case
    for(int r = 0; r < BOARD_DIMENSION; r++); 
    if (column == BOARD_DIMENSION) {
        output.println("The problem has been solved");
        output.println("The program backtracked " + backtrack
                + " times and made " + comparison + " comparisons");

        // CLOSE FILE
        output.close();
        // RETURN TRUE
        return true;
    } else // attempt a solution from column
    {
        output.println("Attempting solution from column " + column);
        for (int i = 0; i < BOARD_DIMENSION; i++) {

            if (isOpen(i, column)) {
                BOARD[i][column] = true;
                comparison++;

                // solve the "rest of the board"
                if (solve(column + 1)) {
                    output.println("The queen has beel placed at [" + i
                            + "] + [" + column + "]");
                    square[((i * 2) + column)].add(imageLabel,
                            BorderLayout.CENTER);// this converts the 2-d
                                                    // array location into a
                                                    // single value to
                                                    // make it compatible with the gui representation of the
                                                    // board
                    return true;
                }

                else {

                    BOARD[i][column] = false;
                    output.println("the queen has been removed from ["+i+"]["+column+"]"); 
                }
            } else
                output.println("The queen cannot be placed at [" + i
                        + "][" + column + "]");
        }
        backtrack++;
        //output.close(); //This is for testing only
        return false;

        // return false to Backtrack to previous column since no squares in
        // current row will solve problem
    }
}

// Method to verify that the square is open.
public boolean isOpen(int row, int column) {
    {
        for (int i = 0; i < BOARD_DIMENSION; i++)// tests for the same row
        {
            if (BOARD[row][i] == true)
                return false;
        }

        for (int i = 0; i < BOARD_DIMENSION; i++)// test for the same column
        {
            if (BOARD[i][column] == true)
                return false;
        }

        for (int i = 0; i < BOARD_DIMENSION; i++)
            // test for diagonal down to the left
            while (row - 1 >= 0 && column - 1 >= 0) {
                if (BOARD[row - 1][column - 1] == true)
                    return false;
            }

        for (int i = 0; i < BOARD_DIMENSION; i++)
            // test for diagonal up to the left
            while (row + 1 < BOARD_DIMENSION && column - 1 >= 0)
                if (BOARD[row + 1][column - 1] == true)
                    return false;
    }
    return true;
}

// output the board
public void printBoard()// this is probably wrong, but
                        // I want to find my mistakes in
                        // the rest of the code before fixing it
{
    for (int i = 0; i < BOARD_DIMENSION; i++)
        for (int j = 0; j < BOARD_DIMENSION; j++) {
            if (BOARD[i][j] == false) {
                System.out.print("*");
                if (i == BOARD_DIMENSION - 1)
                    System.out.print("\n");
            }

            else
                System.out.print("Q");
        }

}

} // end class EightQueens

驱动:

import javax.swing.*;
import java.io.*;
import java.util.*;

public class EightQueensGame {
public static void main(String args[]) throws FileNotFoundException {
    EightQueens eightQueens = new EightQueens();

    eightQueens.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    eightQueens.pack();

    eightQueens.setVisible(true);//just for testing
    if (eightQueens.solve())
    {
        eightQueens.setVisible(true);
        eightQueens.printBoard();


    }

    else
        System.out.println("Sorry, there is no solution");
}
}

2 个答案:

答案 0 :(得分:1)

你在想这个问题。我不得不删除许多代码,因为它是多余的,可以通过更有效的方式实现。检查此Google代码存储库:https://code.google.com/p/8queens/以获取除回溯之外的其他方法。顺便说一句,你违反了MVC规则。你应该已经实现了解算器然后是用户界面,这样你就可以决定改变视图了。

EightQueensSolver.java

public class EightQueensSolver {

    final boolean[][] board ;
    final int[] queensPositions;

    public EightQueensSolver(int boardSize) {
        if(boardSize<4){
            throw new RuntimeException("Can not solve boards of size < 4");
        }
        board = new boolean[boardSize][boardSize];
        queensPositions = new int[boardSize];
    }

    boolean solve(){
        return solve(0);
    }

    boolean solve(int row){
        for(int col=0;col<board.length;col++){

            if(isSafe(row, col)){
                //place the queen for now in this spot
                board[row][col]=true;
                queensPositions[row] = col;
                //if we have finished the board, done.
                if(row==board.length-1) return true;
                //otherwise keep placing queens on next rows
                if(solve(row+1)){
                    //if placing the next queen will make the board solved, OK
                    return true;
                }else{
                    //otherwise backtrack and try other move
                    board[row][col]=false; 
                }
            }

        }
        return false;
    }

    boolean isSafe(int row,int col){
        //no need to test if on the same row, because we 
        //place a single queen on each row.
        //we only need to check if two queens are on 
        //same column or same diagonal
        for(int r=0;r<row;r++){
            for(int c=0;c<board.length;c++){
                if(board[r][c] && (c==col || Math.abs(c-col)==Math.abs(r-row))) 
                    return false;
            }
        }
        return true;
    }


    void printBoard(){
        System.out.println(Arrays.deepToString(board)
                .replace('[', '\n').replace("]", "")
                .replace("true", "Q").replace("false", " ")
        );
    }

    int getQueenColumnPosition(int row){
        return queensPositions[row];
    }
}

EightQueensUI.java

public class EightQueensUI extends JFrame {

    // gui components
    Icon queenIcon;
    JLabel imageLabel;
    EightQueensSolver solver;

    // constructor method
    public EightQueensUI(int boardSize) {
        super("8 Queens: Freddie Style");
        setPreferredSize(new Dimension(800, 800));
        getContentPane().setLayout(new GridLayout(boardSize, boardSize));
        queenIcon = new ImageIcon(getClass().getResource("circle.png"));
        solver = new EightQueensSolver(boardSize);

        solver.solve();

        for(int row=0;row<boardSize;row++){
            for(int col=0;col<boardSize;col++){
                JPanel panel = new JPanel(new BorderLayout());
                panel.setBackground( (col-row)%2 == 0 ? Color.BLACK : Color.WHITE);
                if(solver.getQueenColumnPosition(row)==col){
                    panel.add(new JLabel(queenIcon));
                }
                getContentPane().add(panel);
            }
        }

    } // end constructor

} // end class EightQueensUI

EightQueensGame.java

public class EightQueensGame {
    public static void main(String[] args) throws FileNotFoundException {
        EightQueensUI eightQueens = new EightQueensUI(8);

        eightQueens.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        eightQueens.pack();

        eightQueens.setVisible(true);//just for testing
        eightQueens.solver.printBoard();
    }
}

输出

Q,  ,  ,  ,  ,  ,  ,  , 
 ,  ,  ,  , Q,  ,  ,  , 
 ,  ,  ,  ,  ,  ,  , Q, 
 ,  ,  ,  ,  , Q,  ,  , 
 ,  , Q,  ,  ,  ,  ,  , 
 ,  ,  ,  ,  ,  , Q,  , 
 , Q,  ,  ,  ,  ,  ,  , 
 ,  ,  , Q,  ,  ,  ,  

修改

我对EightQueensSolver进行了一些修改,通过使用列表而不是矩阵使其更有效率,从而为更大的电路板尺寸改善计算时间。

public class EightQueensSolver {

    final int[] queensPositions;

    public EightQueensSolver(int boardSize) {
        if(boardSize<4){
            throw new RuntimeException("Can not solve boards of size < 4");
        }
        queensPositions = new int[boardSize];
        Arrays.fill(queensPositions, -1);
    }

    boolean solve(){
        return solve(0);
    }

    boolean solve(int row){
        for(int col=0;col<queensPositions.length;col++){

            if(isSafe(row, col)){
                //place the queen for now in this spot
                queensPositions[row] = col;
                //if we have finished the board, done.
                if(row==queensPositions.length-1) return true;
                //otherwise keep placing queens on next rows
                if(solve(row+1)){
                    //if placing the next queen will make the board solved, OK
                    return true;
                }else{
                    //otherwise backtrack and try other move
                    queensPositions[row]=-1; 
                }
            }

        }
        return false;
    }

    boolean isSafe(int row,int col){
        //no need to test if on the same row, because we 
        //place a single queen on each row.
        //we only need to check if two queens are on 
        //same column or same diagonal
        for(int r=0;r<row;r++){
            int c = queensPositions[r];
            if(c==col || Math.abs(c-col)==Math.abs(r-row)) 
                return false;
        }
        return true;
    }


    void printBoard(){
        for(int row=0;row<queensPositions.length;row++){
            for(int col=0;col<queensPositions.length;col++){
                if(queensPositions[row]==col) System.out.print("Q,");
                else System.out.print(" ,");
            }
            System.out.println();
        }
    }

    int getQueenColumnPosition(int row){
        return queensPositions[row];
    }
}

答案 1 :(得分:0)

我做了一个比已经给出和接受的答案更为OO的实现。我并不认为它更有效,但代码实际上非常干净且易于阅读。最重要的部分是branch()方法。但同样,它没有以任何方式进行优化,它是蛮力,而对象的创建等使它不是最好的实现。看一下branch()方法,我认为这是实现回溯的一种非常简洁的方法。

import java.util.ArrayList;
import java.util.List;


public class EightQueens {

    public static final int DIMENSION = 20;
    private Board board;

    public EightQueens() {
        board = new Board();
    }

    public void solve() {
        boolean result = branch(0);
        System.out.println(result);
        print();
    }

    private boolean branch(int level) {
        Queen queen = new Queen(level, 0);
        for (int y = 0; y < DIMENSION; y++) {
            queen.setY(y);
            if (board.add(queen)) { // true: adding succeeded. valid position
                if (board.getCount() == DIMENSION || branch(level + 1)) {
                    // cond1: we added the last queen at a valid position, so we're done here.
                    // cond2: not adding the last queen => check if there exists subsolution
                    return true;
                } else {
                    // branch with (level + 1) failed, so remove the queen and go further
                    board.remove(queen);
                }
            }
        }

        return false;
    }

    private void print() {

        for (int y = 0; y < DIMENSION; y++) {
            for (int x = 0; x < DIMENSION; x++) {
                System.out.print(board.queenOn(x, y) ? "x" : "-");
            }
            System.out.print("\n");
        }
    }

    public static void main(String[] args) {
        EightQueens queens = new EightQueens();
        queens.solve();
    }

    // helper objects: Board and Queen
    private class Board {

        private final List<Queen> queens;

        public Board() {
            queens = new ArrayList<Queen>();
        }

        public int getCount() {
            return queens.size();
        }

        public boolean add(Queen queen) {
            for (Queen q : queens) {
                // same row
                if (q.getX() == queen.getX())
                    return false;
                // same column
                if (q.getY() == queen.getY())
                    return false;
                // same diagonal
                if (Math.abs(q.getY() - queen.getY()) == Math.abs(q.getX() - queen.getX()))
                    return false;
            }
            queens.add(queen);
            return true;
        }

        public boolean queenOn(int x, int y) {
            for (Queen queen : queens) {
                if (queen.x == x && queen.y == y) {
                    return true;
                }
            }
            return false;
        }

        public void remove(Queen queen) {
            queens.remove(queen);
        }
    }

    private class Queen {
        private int x, y;

        public Queen(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }

        public void setX(int x) {
            this.x = x;
        }

        public void setY(int y) {
            this.y = y;
        }
    }
}

备注#1:使用DIMENSION等于2或3运行此程序将打印“false”,并打印给定维度的空白板。正确的行为,因为那些维度没有解决方案。

备注#2:此程序仅找到第一个解决方案,并且不会继续寻找更多解决方案。但这当然不是这个答案的重点。