回溯法在包含問題的全部解的解空間樹中,按照深度優先的策略,從根結點出發搜索解空間樹。算法搜索至解空間樹的任何一個結點時,老是先判斷該結點是否確定不包含問題的解,若是確定不包含,則跳過對以該結點爲根的子樹搜索。不然,進入該子樹,繼續按深度優先的策略進行搜索。回溯法在用來求問題的全部解時,要回溯到根,且根結點的全部子樹都已被搜索遍才結束。而回溯法在用來求問題的任一解時,只要搜索到問題的一個解就可結束。算法
回溯法解問題時,首先應明肯定義問題的解空間。問題的解空間應至少包含問題的一個最優解。一般將問題的解空間組織成樹或圖的形式。例如,對於n=3的0-1揹包問題,其解空間樹可用下面的徹底二叉樹表示。
框架
回溯法即以這種工做方式遞歸地在解空間中搜索,直至找到所要求的解或解空間中已無活結點時爲止。函數
在用回溯法搜索解空間樹時,一般採用兩種策略來避免無效搜索,提升回溯法的搜索效率。這兩類方法統稱爲剪枝函數。優化
其一是用約束函數在擴展結點處剪去不知足約束條件的子樹;
其二是用限界法剪去不能獲得最優解的子樹。3d
回溯法是對解空間的深度優先搜索code
void Backtrack(int t) { if(t>n) Output(x); else for(int i=f(n,t);i<=g(n,t);i++) // f(n,t)和g(n,t):在當前擴展結點處未 { x[t]=h(i); //搜索過的子樹的起始編號和終止編號 if (Constraint(t)&&Bound(t)) Backtrack(t+1);} } // Constraint(t)和Bound(t):在當前擴展結點處的約束函數和限界函數
當所給的問題是從n個元素的集合S中找出知足某種性質的子集時,相應的解空間樹爲子集樹。這類子集樹常有2n個葉結點,其結點個數爲2n+1-1。遍歷子集樹的任何算法均需Ω(2n)的計算時間。
blog
void Backtrack(int t) { if(t>n) Output(x); else for(int i=0;i<=1;i++) { x[t]=i; if(Constraint(t)&&Bound(t)) Backtrack(t+1);} }
當所給問題是肯定n個元素的知足某種性質的排列時,相應的解空間樹爲排列樹。排列樹一般有n!各葉結點。所以遍歷排列樹所需的計算時間須要Ω(n!) 的計算時間。
遞歸
void Backtrack(int t) { if(t>n) Output(x); else for(int i=t;i<=n;i++) { Swap(x[t],x[i]); if(Constraint(t)&&Bound(t)) Backtrack(t+1); Swap(x[t],x[i]); } }
注:因此剪枝函數須要好好想一想,以到達優化。當解題時,能夠先將解空間都描繪出來,再慢慢肯定剪枝函數class