【回顧&小結】回溯法

回溯法の定義

回溯法(探索與回溯法)是一種選優搜索法,又稱爲試探法,按選優條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步從新選擇,這種走不通就退回再走的技術爲回溯法。算法

以上來自百度百科。數組

回溯法の解空間

就是要找出這個問題全部的解,在這些解裏面篩選出最優解
舉個例子,0-1揹包的問題,假設咱們有A,B,C三個物品,咱們要判斷選擇裝進哪些物品。從咱們的揹包裏沒有任何東西開始,咱們按順序一個個物品來判斷是否是把它裝進去。那麼咱們就可能出現兩種想法:
想法一
對每個物品判斷是裝仍是不裝,而後畫出一個不規範的的圖:
圖片描述函數

在這個圖中,連線旁邊的數字:1表示裝入揹包中,0表示不裝入揹包。第一層中,咱們判斷是否放入A,放入就是1那條岔路,反之是0那條岔路,因此很直觀地,咱們就能夠獲得全部可能出現的8個狀況:spa

{(1,1,1),(1,1,0),(1,0,1),(1,0,0),(0,1,1),(0,1,0),(0,0,1),(0,0,0)}

想法二
咱們假設本身一開始要從三個裏面選一個來裝入,因此就有了:
圖片描述code

(可是01揹包問題並不適合用這種方法來解,因此這裏就是與上面的樹作一個對比)
如上圖則表示,咱們從A,B,C三個中先選擇一個裝入,若是第一輪選擇了A,那麼剩下就還有選擇B或者Cblog

而想法一的想法,其實就能造成一顆子集樹,它在每次選擇的時候,是在一個有限選擇集合S裏面進行選擇,01揹包中,這個S就是{0,1},而想法二能造成一顆排列樹,它在每次進行選擇的時候是在給出的選項S中做選擇,這裏是{A,B,C}。
因此回溯法的寫法,也有兩種,子集樹的寫法和排列樹的寫法,作題的時候要本身選擇更加合適的方法來解決。遞歸

回溯法の公式

每種算法均可以選擇遞歸和迭代的方式來寫,顯然這裏用迭代方式來寫是很麻煩的,能夠說也沒有人選擇這樣的方法,因此咱們都採用遞歸來寫。圖片

子集樹寫法
咱們的目的就是要遍歷子集樹上的每一個解法,爲了判斷每個解對不對,因此咱們應該是深度遍歷,因此咱們須要:t(樹的深度),數組x(每一層的結果選擇)。先寫出中心的遍歷函數:it

void backtrack(int t){
    if(t>=n){ 
           output();   //此時已到達葉子結點,用於輸出一個結果
           return;
    }
    
    //對分叉點遍歷0,1兩種狀況    
    for(int i=0;i<=1;i++){
        x[t]=i;  //給第t層表明的選擇賦可能選擇的值
        if(ok(t))  
            //ok是判斷目前的一個解有可行的機會 
            backtrack(t+1);  
    }
        
}

此時咱們應該注意,x數組的初始值應該都爲0。for循環

排列樹寫法
也和子集樹同樣要遍歷子集樹上的每一個解法,咱們一樣須要:t(樹的深度),數組x(每一層的結果選擇)。寫出中心的遍歷函數:

void backtrack(int t){
    if(t>=n){ 
           output();   //此時已到達葉子結點,用於輸出一個結果
           return;
    }
    
    //從第t個單元開始進行交換排列  
    for(int i=t;i<n;i++){
        swap(x[t],x[i]);  //交換兩個排列順序,獲得了一個解
        if(ok(t))  
            //ok是判斷目前的一個解有可行的機會 
            backtrack(t+1);  
        swap(x[t],x[i]);  //恢復原樣,給後面的好繼續交換判斷
    }
        
}

此時咱們應該注意,由於排列樹其實就是寫出全部可能的排列,因此x數組裏面每一個單元的內容都應該是不同的,咱們在初始化的時候就應該依次賦予可能的解。
總結比較,在子集樹的公式中,for循環中的i是可能的取值,排列樹的公式中,for循環中的i是全部可能取值的存放位置

舉個栗子

n皇后問題,要求在一個n行n格的棋盤中擺放n個皇后,皇后不能在同一行,同一列或者同一斜線,咱們要找出有多少個解法。這個問題,咱們在選擇x數組上,一個一維數組就足夠用,下標表示在第幾行,對應的值表示在第幾列。
子集樹方法
假如n=4,初始化x數組中每一個取值爲0,那麼咱們的for循環中i可能有0,1,2,3四種選擇,而後就是填寫ok()函數,根據約束的條件可知,ok()函數裏應該填寫判斷

皇后不能在同一行,同一列或者同一斜線

圖片描述

排列樹方法
假如n=4,初始化x數組中每一個取值爲0,1,2,3,ok函數也和子集樹同樣,咱們的for循環中i也可能有0,1,2,3四種選擇,由於是採用了交換,不出現兩個重複的值,因此和子集樹相比,走過的路要少一些。
圖片描述

效率の提升

爲了提升算法的效率,咱們能夠從ok()函數來入手:

  1. 計算簡單
  2. 剪枝多(避免走行不通的路)

可是這兩個條件是互相矛盾的。

最後...

這是一個掙扎的小渣渣寫的文章,因此可能會有錯誤,各位大佬多指教QAQ

相關文章
相關標籤/搜索