回溯法
有許多問題,當需要找出它的解集或者要求回答什麼解是滿足某些約束條件的最優解時,往往使用回溯法。
回溯法的基本做法就是搜尋,或者是一種能避免不必要搜尋的暴力搜尋法,這種方法適用於一些組合數相當大的問題。
問題的解空間
問題的解向量:回溯法希望乙個問題的解能夠表示成乙個n元式(x1,x2,…xn)
顯約束:對分量xi的取值進行限定
隱約束:為滿足問題的解對不同分量之間施加的約束。
解空間:對於乙個問題的例項,解向量滿足顯約束條件的所有多元組,構成了該例項的乙個解空間。
ps:同一問題可以有多種比奧斯,有些比表示方法更簡單,所需表示的狀態空間更小(儲存量少,搜尋方法簡單)
下面結合乙個例子,來分清楚這些概念:
n=3時的0-1揹包問題用完全二叉樹表示的解空間:
回溯法:為了避免生成那些不可能產生最佳解的問題狀態,要不斷地利用限界函式(bounding function)來剪枝那些實際上不可能產生所需解的活節點,以減少問題的計算量。具有限界函式的深度優先法稱為回溯法。(回溯法 = 窮舉 +剪枝)
回溯法的基本思想
針對所給問題,定義問題的解空間
確定易於搜尋的解空間結構
以深度優先方式搜尋解空間,並在搜尋過程中用剪枝函式避免無效搜尋
兩個常用的剪枝函式:
1. 約束函式: 在擴充套件節點處減去不滿足約束的子樹
2. 限界函式: 減去得不到最優解子樹
用回溯法解題的乙個顯著特徵就是在搜尋過程中動態產生問題的解空間。在任何死後,演算法只儲存了從根節點到當前擴充套件節點的路徑。如果解空間樹中從根節點到葉節點的最長路徑的長度為h(n),則回溯法所需的計算空間通常為o(h(n))。而顯式的儲存整個解空間則需要o(2^h(n))或o(h(n)!)記憶體空間。
遞迴回溯
回溯法對解空間做深度優先搜尋,因此,在一般情況下用遞迴方法實現回溯法。
針對n叉樹的遞迴回溯方法
void backtrack(int t)
else}}
迭代回溯
採用樹的非遞迴深度優先遍歷演算法,可將回溯法表示成乙個非遞迴迭代過程。
針對n叉樹的迭代回溯方法
void iterativebacktrack ()
else
//未找到,向更深層次遍歷
t ++;}}
}else
t --;}}
回溯法一般依賴的兩種資料結構:子集樹和排列樹
子集樹(遍歷子集樹需o(2^n)計算時間)
}排列樹(遍歷排列樹需要o(n!)計算時間)
}下邊結合具體問題:裝載問題
有一批共n個貨櫃要裝上2艘載重量分別為c1和c2的輪船,其中貨櫃i的重量為wi,且
裝載問題要求確定是否有乙個合理的裝載方案可將這個貨櫃裝上這2艘輪船。如果有,找出一種裝載方案。
解決方案:
容易證明,如果乙個給定裝載問題有解,則採用下面可得到最優裝載方案。
(1)首先將第一艘輪船盡可能裝滿;
(2)將剩餘的貨櫃裝上第二艘輪船;
將第一艘輪船盡可能裝滿等價於選取全體貨櫃的乙個子集,使該子集中貨櫃之和最接近。由此可知,裝載問題等價於以下特殊的0-1揹包問題。
解空間:子集樹
可行性約束函式(選擇當前元素):
上界函式(不選擇當前元素):
當前載重量cw + 剩餘貨櫃的重量r<= 當前最優載重量bestw
void backtrack (int i)
if (cw + r > bestw)
r += w[i];
}
變數解釋:
r: 剩餘重量
w: 各個貨櫃重
cw:當前總重量
x: 每個貨櫃是否被選取標誌
bestx: 最佳選取方案
bestw: 最優載重量
#include
#include
#include
using
namespace
std;
/* 裝載問題子函式
* layers: 搜尋到第layers層結點
* layers_size: layers_size總層數
* current_w: 當前承載量
* best_w: 最優載重量
* flag_x: 選取方案
* best_x: 最佳選取方案
* remainder_w:剩餘重量
* container_w:每個貨櫃的重量
* total_w: 總承載量
*/void __backtrack (int layers, const
int layers_size, int current_w,int& best_w, vector
& flag_x, vector
&best_x,int remainder_w,const
vector
& container_w,int total_w)
return ;
}remainder_w -= container_w[layers];
if (current_w + container_w[layers] <= total_w)
if (current_w + remainder_w > best_w || best_w == -1)
remainder_w += container_w[layers];
}/* 裝載問題
* container_w: 各個貨櫃重量
* total_w: 總承載量
*/void loading_backtrack (int total_w, vector
& container_w)
}cout
<< ")"
<< endl;
}int main()
回溯之子集樹和排列樹
1.當所給問題是從n個元素的集合s中找出s滿足某種性質的子集時,相應的解空間稱為子集樹。例如 n個物品的0 1揹包問題所相應的解空間是一棵子集樹,這類子集樹通常有2 n個葉結點,其結點總數為 2 n 1 1。遍歷子集樹的演算法通常需奧秘加 2 n 計算時間。回溯法搜尋子集樹的演算法一般可以描述如下 ...
子集樹與排列樹
1.當所給問題是從n個元素的集合s中找出s滿足某種性質的子集時,相應的解空間稱為子集樹。例如 n個物品的0 1揹包問題所相應的解空間是一棵子集樹,這類子集樹通常有2 n個葉結點,其結點總數為 2 n 1 1。遍歷子集樹的演算法通常需要 2 n 計算時間。回溯法搜尋子集樹的演算法一般可以描述如下 vo...
子集樹與排列樹
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!在前面的利用回溯法求解01揹包問題的時候,我們提到了這個問題的解空間樹是子集樹,那麼什麼是子集樹呢?與子集樹對應的還有乙個排列樹!它們又有什麼區別呢?為了說明這兩個概念的區別,我們首先假定有乙個集合s 解空間為排列樹的典型問題就是旅行售貨員問題。簡...