回溯法 演算法框架及應用

2021-10-24 05:14:37 字數 3318 閱讀 8581

在包含問題的所有解的空間樹中,按照深度優先搜尋策略,從根節點出發搜尋解空間樹。

活結點:自身已生成但其孩子結點沒有全部生成的結點

擴充套件結點:指正在產生孩子結點的結點,e結點

死結點:指其所有結點均已產生的節點

首先根節點成為活結點,同時也成為當前的擴充套件結點

在當前的擴充套件結點處,搜尋向縱深方向移至乙個新結點。這個新結點就成為新的活結點,並成為當前擴充套件結點。如果在當前擴充套件結點處不能在向縱深方向移動,則當前擴充套件結點就成為死結點。此時應往回移動(回溯)至最近的乙個活結點處,並使這個結點成為當前可擴充套件結點。

回溯法:以這種方式遞迴地在解空間中搜尋,直到找到所有要求的解或解空間中已無活結點為止。

當從狀態si搜尋到s i+1後,s i+1變為死結點,則從狀態s i+1回退到si,再從si找其他可能路徑

若用回溯法求問題的所有解,需要回溯到根節點,且根節點的所有可行子樹都已被搜尋完才結束。若用回溯法求問題的任一解,只要搜尋到問題的乙個解就可以結束。

由於採用回溯法求解時存在退回到祖先結點的過程,所以需要儲存搜尋過的結點。通常有兩種方法,

其一是用自定義棧儲存祖先結點;

其二是採用遞迴法,因為遞迴呼叫會將祖先結點儲存到系統棧中,在遞迴呼叫返回時自動回退到祖先結點。

另外,用回溯法搜尋解空間時通常採用兩種策略避免無效搜尋,以提高回溯的搜尋效率,

法一:用約束函式在擴充套件結點處減除不滿足約束條件的路徑;

法二:用限界函式減去得不到的問題解或最優解的路徑

上述兩種方法統稱剪枝函式。

1.針對對給定的問題的解空間樹,問題的解空間樹應至少包含問題的乙個解或者最優解。

2.確定結點的擴充套件搜尋規則

3.以深度優先方式搜尋解空間樹,並在搜尋過程中可以採用剪枝函式來避免無效搜尋。其中,深度優先方式可以選擇遞迴回溯或者迭代(非遞迴)回溯。

設問題的解是乙個n維向量(x1,x2,…,xn),約束條件是xi滿足某種條件,記為constraint(xi);限界函式是xi應滿足某種條件,記為bound(xi),回溯法的演算法通常分為非遞迴回溯框架和遞迴回溯框架

i:對應解空間的第i層的某個結點

int x[n]

; //x存放解向量,全域性變數

void backtrack(int n)}}

else //不存在子結點,返回上一層,即回溯

i--;

}}

回溯法是對解空間的深度優先搜尋,因為遞迴演算法中的形參具有自動回退(回溯)的能力,所有許多回溯設計的演算法都設計成遞迴演算法,比同樣的非遞迴演算法設計起來更加簡便。

其中,i為搜尋的層次(深度),通常從0或1開始,分兩種情況:

(1)解空間為子集樹

一般地,解空間為子集樹的遞迴回溯框架如下:

int x[n]

; //x存放解向量,全域性變數

void backtrack(int n)

}}

採用上述演算法框架需注意以下幾點:

(1)i從1開始呼叫上述回溯演算法框架,此時根結點為第1層,葉子結點為第n+1層,當然i也可以從0開始,這樣根結點為第0層,葉子結點為第n層,所以需要將上述**中的

if(i>n)

改為if(i>=n)

(2)在上述框架中通過for迴圈使用j列舉i的所有可能路徑,如果擴充套件路徑只有兩條,可以改為兩次遞迴呼叫(例:求解0/1揹包問題、子集和問題等)

(3)這裡回溯框架只有 i 這 乙個引數,在實際應用中可以根據情況設定多個引數。

例:在乙個含有n個整數的陣列a,所有元素均不相同,設計乙個演算法求其所有子集(冪集)

例如:a[ ]=,所有子集是{},,,,,,,

思路:採用回溯法

問題的解空間為子集樹,每個元素只有兩種擴充套件,要麼選擇,要麼不選。

採用深度優先搜尋思路,解向量為x[ ],x[i]=0表示不選擇a[i]。用i掃瞄陣列a,也就是說問題初始狀態是(i=0,x的元素均為0),目標狀態是(i=n,x為乙個解)。從狀態(i,x)可以擴充套件出兩個狀態。

(1)不選擇a[i]元素——>下乙個狀態為(i+1,x[i]=0)

(2)選擇a[i]元素——>下乙個狀態為(i+1,x[i]=i)

這裡i總是遞增的,所以不會出現狀態重複的情況。

法一:標準解向量**:

#include

#include

#define maxn 100

void dispsolution(int a[

],int n,int x[

]) //輸出乙個解")

;} void dfs(int a[

],int n,int i,int x[

]) //用回溯法求解解向量x }

void main(

); //s[0 ... n-1]為給定的字串,設定為全域性變數

int n=sizeof(a)/sizeof(a[0]);

int x[maxn]

; //解向量

memset(x,0,sizeof(x))

; //解向量初始化

printf(

"求解結果\n");

dfs(a,n,0,x)

; printf(

"\n");

}

法二:不採用標準解向量,直接求結果

#include

#include

using namespace std;

void dispsolution(vector < int > path) //輸出乙個解")

;} void dfs(int a[

],int n,int i,vector < int > path) //用回溯法求子集path }

int main(

); //s[0 ... n-1]為給定的字串,設定為全域性變數

int n=sizeof(a)/sizeof(a[0]);

vector< int > path;

printf(

"求解結果\n");

dfs(a,n,0,path)

; printf(

"\n");

}

回溯法演算法框架

回溯法 有通用解題法 之稱,可以系統的搜尋乙個問題的所有解和任一解,是乙個既帶有系統性,又帶有跳躍性的搜尋演算法。演算法基本思想 確定解空間後 從開始節點出發,以深度優先的方式搜尋整個解空間。如果當前擴充套件結點不能再向縱深方向移動,當前節點為死節點。此時,應該往回移動至最近的乙個活節點處。並是這個...

回溯法演算法框架

回溯法 有通用解題法 之稱,可以系統的搜尋乙個問題的所有解和任一解,是乙個既帶有系統性,又帶有跳躍性的搜尋演算法。演算法基本思想 確定解空間後 從開始節點出發,以深度優先的方式搜尋整個解空間。如果當前擴充套件結點不能再向縱深方向移動,當前節點為死節點。此時,應該往回移動至最近的乙個活節點處。並是這個...

回溯法的演算法框架

如下 def constraint 約束函式 return true defbound 限界函式 return true defbacktracing t,lst,temp 回溯法函式 size len lst if t size print temp else for i in range 0,s...