力扣解析->回溯算法經典之N皇后問題

1.問題起源->八皇后問題

該問題是國際西洋棋棋手馬克斯·貝瑟爾於1848年提出:在8×8格的國際象棋上擺放八個皇后,使其不能互相攻擊:即任意兩個皇后都不能處於同一行、同一列或同一斜線上,求解符合條件的解的個數。

2.能否暴力窮舉?

國際象棋的格子數有64個,我們的任務是從中取8個,這是一個排列組合的問題,暴力搜索的次數是:

雖然能夠解決,但是明顯過於笨拙;

3.觀察特徵,減少枚舉總數

我們通過觀察和歸納,可以發現:

如果使得即任意兩個皇后都不能處於同一行、同一列或同一斜線上,那麼恰好每行,每列 都只有一個皇后

我們只考慮行和列的情況:

在第0行,皇后可擺放的位置有8種;

在第1行,皇后可擺放的位置有7種 ;

在第2行,皇后可擺放的位置有6種;

     ........

因此,在不考慮對角線的情況下,

枚舉的全集爲:8!=40320 種

較暴力窮舉優異的多,因此我們就從此入手 

4.巧用一維數組,簡化數據結構

我們可以用二維數組表示棋盤格,但是會增加數據結構的複雜性,

我們可以充分利用數組的下標和值來建立求解的數據結構。

我們定義一個長度爲8的數組:

其下標代表行的編號;

其值代表列的編號;

這樣相關的比較操作就可以使用一維數組進行

5.算法總體設計

首先,我們需要遍歷棋盤的每一行

在每一行,我們要嘗試每一列;

每一次列的嘗試之後,都要檢查是否符合條件

如果符合,走下一行(遞歸調用此算法);

如果不符合,走下一列; 如果所有列都不符合,返回上一層遞歸;

爲啥能返回?因爲都執行完了,本層被請出棧了,就到上一層了(上一層的列循環還沒有做完,因此上一層不會被從棧裏面請出去);

如果不符合條件,我們的選擇是返回上一層,而不是走下一次遞歸,這種解法稱爲回溯

5.1 遞歸結構設計

 //cur 當前行
    public static void que(int cur) {
    	if(cur==n) {
                          tot++;
    	}else {
    		//i代表當前列
    		for(int i=0;i<n;i++) { 
    			boolean flag=true;
    			temp[cur]=i;
    			//j代表之前已經存在的皇后
    			for(int j=0;j<cur;j++) {
    				if(不符合條件) {
    					flag=false;
    					break;//看下一列
    				}
    			}
    			if(flag) {
    				que(cur+1);//符合條件,下一行
    			}
    			
    		}
    	}
    }

5.3如何判斷是否發生攻擊

我們外層是按照行來遍歷的,因此不需要檢查是否同行;

我們需要檢查是否同列,是否同對角線:

我們的檢查數組: 其下標代表行的編號; 其值代表列的編號; 不同列,即任意兩個數組元素的值不能相等;

不同對角線,怎麼判斷

斜率呀!

如果A點座標爲(x1,y1);B點座標爲(x2,y2)

那麼 A與B在對角線的情況就是 |y1-y2|=|x1-x2|

能不能直接用斜率?可以但是分子爲0需要特殊處理

因此直接用 |y1-y2|=|x1-x2| 

6.擴展-N皇后問題(題目來自力扣)

代碼:

class Solution {
    static int n;
    static int tot=0;
    static int[]temp; 
    public static void que(int cur) {
    	if(cur==n) {
    		tot++;
    	}else {
    		//i代表當前列
    		for(int i=0;i<n;i++) { 
    			boolean flag=true;
    			temp[cur]=i;
    			//j代表之前的
    			for(int j=0;j<cur;j++) {
    				if(temp[cur]==temp[j]||Math.abs(temp[cur]-temp[j])==Math.abs(cur-j)) {
    					flag=false;
    					break;
    				}
    			}
    			if(flag) {
    				que(cur+1);
    			}
    			
    		}
    	}
    }
    public static void clear() {
    	n=0;
    	tot=0;
    	temp=new int[n];
    }
    public int totalNQueens(int m) {
      if(m<=0){
          return 0;
      }
      if(m==1){
          return 1;
      }
      n=m;
      temp=new int[n];
      que(0);
      int target=tot;
      clear();
      return target;
    }
}

結果:

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