八皇后問題的遞歸解法(最易理解的版本)

八皇后問題是一個古來而著名的問題,該問題是19世紀著名的數學家高斯同窗提出來的。在8*8的國際象棋上擺放八個皇后,使其不能互相的攻擊,也就是說,任意的兩個皇后不能放在同一行或則是同一個列或者是同一個對角線上,問有多少個擺放的方法


本算法的思路是按行來規定皇后位置,第一行放置一個皇后,第二行放置一個皇后, 第N行也放置一個皇后… 這樣, 能夠保證每行都有一個皇后,那麼各行的皇后應該放置在那一列呢, 算法經過循環來完成,在循環的過程當中, 一旦找到一個合適的列,則該行的皇后位置肯定,則繼續進行下一行的皇后的位置的肯定。因爲每一行肯定皇后位置的方式類似,因此可使用遞歸法。一旦最後 一行的皇后位置肯定,則能夠獲得一組解。找到一組解以後, 以前肯定皇后應該放置在哪一列的循環其實才進行了一輪循環的, 算法經過該循環遍歷全部的列,以此肯定每一行全部可能的列的位置。在從一輪循環進入下一輪循環以前,算法須要清除在上一輪被標記爲不可放置皇后的標記,也就是回溯。由於進入下一輪循環以後,同一行的皇后的列的位置會發生了變化,以前被標記爲不可放置皇后的列和正反對角線位置都已經失效。 java

 

 

 

public class EightQueenSolver {


    private int QUEEN_COUNT = 0; // 皇后的默認數量

    private int[][] Queencount;// 分配8X8的數組,充當棋盤,存放皇后

    private int resultCount = 0;// 記錄皇后的放置方法的總數

    private int[] Queenplace;// 對於索引n, Queenplace[n]表示第n行的皇后放置位置是第Queenplace[n]列

    public EightQueenSolver(int n) {
        this.QUEEN_COUNT = n;
        this.resultCount = 0;
        this.Queencount = new int[QUEEN_COUNT][QUEEN_COUNT];
        Queenplace = new int[QUEEN_COUNT];
    }

    public void putQueen() {
        putQueen(0);
    }

    private void putQueen(int row0) {
        int row = row0;// 行標
        for (int column = 0; column < QUEEN_COUNT; column++)// column表示列標,該層循環的做用是用於詢問第row行,第column列是否能夠放置皇后
        {
            if (Queencount[row][column] == 0)// 若是第row行、第column列能夠放皇后
            {
                for (int nextRow = row + 1; nextRow < QUEEN_COUNT; nextRow++)// 該層for循環的做用是使其斜下方和正下方不爲0
                {
                    //將不一樣行的同一列(正下方)標記爲非零,表示不能再在該列放置皇后了
                    Queencount[nextRow][column]++;

                    //經過該層循環將第row行、第column列的正對角線上的位置標記爲非零,表示不能在這些位置放置皇后
                    if (column - nextRow + row >= 0) {
                        Queencount[nextRow][column - nextRow + row]++;
                    }

                    //經過該層循環將第row行、第column列的反對角線上的位置標記爲非零,表示不能在這些位置放置皇后
                    if (column + nextRow - row < QUEEN_COUNT) {
                        Queencount[nextRow][column + nextRow - row]++;
                    }
                }
                // 記錄下第row行第column列放置了皇后
                Queenplace[row] = column;

                // 若是各行都放置了皇后,也就是說若是皇后已放滿,打印出皇后佈局
                if (row == QUEEN_COUNT - 1)
                {
                    printQueen(++resultCount);
                } else // 不然遞歸繼續排列下一行皇后
                {
                    putQueen(row + 1);
                }
                for (int rows = row + 1; rows < QUEEN_COUNT; rows++)// 回溯,使得在第row行的皇后不放在第column列,那放置在那一列? 
                                                                    // 答案是經過該算法的最外層循環,利用最外層for循環將皇后放在這一行的其餘列
                {
                    //既然第row行、第column列不放置皇后了,則須要恢復正下方的不可用標記,將不一樣行的同一列的非零標記還原,也即恢復該位置的正下面的標記
                    Queencount[rows][column]--;

                    //還原第row行、第column列的正對角線上的位置標記
                    if (column - rows + row >= 0) {
                        Queencount[rows][column - rows + row]--;
                    }
                    //還原第row行、第column列的反對角線上的位置標記
                    if (column + rows - row < QUEEN_COUNT) {
                        Queencount[rows][column + rows - row]--;
                    }
                }
            }
        }
        if (row == 0) {
            System.out.println(QUEEN_COUNT + "皇后問題共有" + resultCount + "個解.");
        }
    }

    private void printQueen(int size)// 打印皇后佈局
    {
        System.out.println(QUEEN_COUNT + "皇后的第" + size + "個解是:");
        System.out.println();
        for (int row = 0; row < QUEEN_COUNT; row++) {
            for (int column = 0; column < QUEEN_COUNT; column++) {
                System.out.print(Queenplace[row] == column ? " * " : " - ");
            }
            System.out.println();
        }
        System.out.println();
    }

    public static void main(String[] args) {
        EightQueenSolver eq = new EightQueenSolver(8);
        eq.putQueen();
    }
}
相關文章
相關標籤/搜索