1. 程式人生 > >回溯算法

回溯算法

style pan 集合 空間復雜度 pos 還需 工作方式 con bsp

回溯法解題的關鍵要素

確定了問題的解空間結構後,回溯法將從開始結點(根結點)出發,以深度優先的方式搜索整個解空間。開始結點成為活結點,同時也成為擴展結點。在當前的擴展結點處,向縱深方向搜索並移至一個新結點,這個新結點就成為一個新的活結點,並成為當前的擴展結點。如果在當前的擴展結點處不能再向縱深方向移動,則當前的擴展結點就成為死結點。此時應往回移動(回溯)至最近的一個活結點處,並使其成為當前的擴展結點。回溯法以上述工作方式遞歸地在解空間中搜索,直至找到所要求的解或解空間中已無活結點時為止。 運用回溯法解題的關鍵要素有以下三點: (1) 針對給定的問題,定義問題的解空間; (2) 確定易於搜索的解空間結構;
(3) 以深度優先方式搜索解空間,並且在搜索過程中用剪枝函數避免無效搜索。

遞歸和叠代回溯

一般情況下可以用遞歸函數實現回溯法,遞歸函數模板如下: void BackTrace(int t) { if(t>n) Output(x); else for(int i = f (n, t); i <= g (n, t); i++ ) { x[t] = h(i); if(Constraint(t) && Bound (t)) BackTrace(t+1); } } 其中,t 表示遞歸深度,即當前擴展結點在解空間樹中的深度;n 用來控制遞歸深度,即解空間樹的高度。當 t>n時,算法已搜索到一個葉子結點,此時由函數Output(x)對得到的可行解x進行記錄或輸出處理。用 f(n, t)和 g(n, t)分別表示在當前擴展結點處未搜索過的子樹的起始編號和終止編號;h(i)表示在當前擴展結點處x[t] 的第i個可選值;函數 Constraint(t)和 Bound(t)分別表示當前擴展結點處的約束函數和限界函數。若函數 Constraint(t)的返回值為真,則表示當前擴展結點處x[1:t] 的取值滿足問題的約束條件;否則不滿足問題的約束條件。若函數Bound(t)的返回值為真,則表示在當前擴展結點處x[1:t] 的取值尚未使目標函數越界,還需由BackTrace(t+1)對其相應的子樹做進一步地搜索;否則,在當前擴展結點處x[1:t]的取值已使目標函數越界,可剪去相應的子樹。
采用叠代的方式也可實現回溯算法,叠代回溯算法的模板如下: void IterativeBackTrace(void) { int t = 1; while(t>0) { if(f(n, t) <= g( n, t)) for(int i = f(n, t); i <= g(n, t); i++ ) { x[t] = h(i); if(Constraint(t) && Bound(t)) { if ( Solution(t)) Output(x); else t++; } } else t? ?; } } 在上述叠代算法中,用Solution(t)判斷在當前擴展結點處是否已得到問題的一個可行解,若其返回值為真,則表示在當前擴展結點處x[1:t] 是問題的一個可行解;否則表示在當前擴展結點處x[1:t]只是問題的一個部分解,還需要向縱深方向繼續搜索。用回溯法解題的一個顯著特征是問題的解空間是在搜索過程中動態生成的,在任何時刻算法只保存從根結點到當前擴展結點的路徑。如果在解空間樹中,從根結點到葉子結點的最長路徑長度為 h(n),則回溯法所需的計算空間復雜度為 O(h(n)),而顯式地存儲整個解空間復雜度則需要O(2h(n)
)或O(h(n)!)。
來源: https://baike.baidu.com/item/%E5%9B%9E%E6%BA%AF%E6%B3%95/86074?fr=aladdin

子集樹與排列樹

當給定的問題是從n個元素的集合S中找出滿足某種性質的子集時,相應的解空間樹稱為子集樹。例如,n個物品的0-1 背包問題所對應的解空間樹是一棵子集樹,該類樹通常有2n個葉子結點,總結點數為2n+1- 1,遍歷子集樹的任何算法需要的計算時間復雜度均為O(2n)。 回溯法搜索子集樹的一般算法描述如下: void BackTrace(int t) { if(t>n) Output(x); else for(int i = 0; i <= n; i++) { x[t] = i; if(Contraint(t) && Bound(t)) BackTrace (t + 1); } } 當給定的問題是確定 n 個元素滿足某種性質的排列時,對應的解空間樹稱為排列樹。排列樹通常有n! 個葉子結點,遍歷排列樹需要的計算時間復雜度為O(n!)。 回溯法搜索排列樹的算法模板如下: void BackTrace(int t) { if(t>n) Output(x); else for(int i = 0; i <= n; i++) { Swap(x[t], x[i]); if(Contraint (t) && Bound (t)) BackTrace(t + 1); Swap(x[t], x[i]); } }

回溯算法