【算法設計與分析】N皇后(回溯算法)

回溯VS遞歸

        很多人認爲回溯和遞歸是一樣的,其實不然。在回溯法中可以看到有遞歸的身影,但是兩者是有區別的。

        回溯法從問題本身出發,在包含問題的所有可能解的解空間樹中,從根結點出發,按照深度優先的策略進行搜索,對於解空間樹的某個結點,若滿足約束條件,則進入該子樹繼續搜索,否則將以該結點爲根結點的子樹進行剪枝,逐層向其祖先結點回溯,尋找可能實現的所有情況。和窮舉法的思想相近,不同在於窮舉法是將所有的情況都列舉出來以後再一一篩選,而回溯法在列舉過程如果發現當前情況根本不可能存在,就停止後續的所有工作,返回上一步進行新的嘗試。

        遞歸從問題的結果出發,例如求 n!,要想知道 n!的結果,就需要知道 n*(n-1)! 的結果,而要想知道 (n-1)! 結果,就需要提前知道 (n-1)*(n-2)!。這樣不斷地向自己提問,不斷地調用自己的思想就是遞歸。

        回溯和遞歸唯一的聯繫就是,回溯法可以用遞歸思想實現。


N皇后問題 
在n x n的棋盤上擺放n個皇后,而且n個皇后中的任意兩個是不能處於同一行、同一列、或同一斜線上。

【分析】因爲n皇后不能在同行,同列, 同斜線。

  1. 每一行放一個皇后,就解決了不在同行的問題。
  2. 在第i行的時候,遍歷n列,試探位置。和之前所有行放的位置進行比較。
  3. 比較列:當前列 col 不等於之前所有列。 即col != arr[i] 。
  4. 比較斜線, 因爲不再同一斜率爲1或者-1的斜線。(row - i) / (col - arr[i]) != 1 或 -1
  5. 可以取巧用絕對值函數: abs(row-i) != abs(col-arr[i]) 。
     
public class NQueen {
    private int n;      // n個皇后
    private long sum;   // 可行解的數量
    private int[] arr;  // 當前的解

    public NQueen(int n){
        this.n = n;
        sum = 0;
        arr = new int[n+1];
    }
    
    // row col   i  arr[i]
    public boolean Check(int row, int col){
        // 每一行放一個,一定不在同一行
        for(int i = 1; i < row; i++){ 
            // 當前列是否與第一,二...列相同
        	// 當前點是否與第一,二...列斜率爲正負1
            if(col == arr[i] || Math.abs(row - i) == Math.abs(col - arr[i])) 
                return false;
        }
        return true;
    }

    public void FindNQueen(int k) {
        if (k > n) {   //遍歷到葉子節點,找出一種解, sum+1
            sum++;
            return;
        }
        for (int i = 1; i <= n; i++) {
            if (Check(k, i)) {   //檢查是否滿足條件
                arr[k] = i;      //記錄
                FindNQueen(k + 1);   //遞歸查找
            }
        }
    }

    public static void main(String args[]){
    	System.out.println("請輸入n的值:");
    	Scanner sc = new Scanner(System.in);
    	int n = sc.nextInt();
        NQueen nQueen = new NQueen(n);
        nQueen.FindNQueen(1);
        System.out.println(nQueen.sum);
    }
}

當N=8時,輸出結果爲92。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章