P11 揹包問題的搜尋解法

2021-09-08 21:37:21 字數 1594 閱讀 8689

對於01揹包問題,簡單的深搜的複雜度是o(2^n)。就是列舉出所有2^n種將物品放入揹包的方案,然後找最優解。基本框架如下:

procedure searchpack(i,cur_v,cur_w)
if(i>n)
if(cur_w>best)
best=cur_w
return
if(cur_v+v[i]<=v)
searchpack(i+1,cur_v+v[i],cur_w+w[i])
searchpack(i+1,cur_v,cur_w)
其中cur_v和cur_w表示當前解的費用和權值。主程式中呼叫searchpack(1,0,0)即可。

基本的剪枝方法不外乎可行性剪枝或最優性剪枝。

可行性剪枝即判斷按照當前的搜尋路徑搜下去能否找到乙個可行解,例如:若將剩下所有物品都放入揹包仍然無法將揹包充滿(設題目要求必須將揹包充滿),則剪枝。

最優性剪枝即判斷按照當前的搜尋路徑搜下去能否找到乙個最優解,例如:若加上剩下所有物品的權值也無法得到比當前得到的最優解更優的解,則剪枝。

在搜尋中,可以認為順序靠前的物品會被優先考慮。所以利用貪心的思想,將更有可能出現在結果中的物品的順序提前,可以較快地得出貪心地較優解,更有利於最優性剪枝。所以,可以考慮將按照「價效比」(權值/費用)來排列搜尋順序。

另一方面,若將費用較大的物品排列在前面,可以較快地填滿揹包,有利於可行性剪枝。

最後一種可以考慮的方案是:在開始搜尋前將輸入檔案中給定的物品的順序隨機打亂。這樣可以避免命題人故意設定的陷阱。

以上三種決定搜尋順序的方法很難說哪種更好,事實上每種方法都有適用的題目和資料,也有可能將它們在某種程度上混合使用。

子集和問題是乙個np-complete問題,與前述的(加權的)01揹包問題並不相同。給定乙個整數的集合s和乙個整數x,問是否存在s的乙個子集滿足其中所有元素的和為x。

這個問題有乙個時間複雜度為o(2^(n/2))的較高效的搜尋演算法,其中n是集合s的大小。

第一步思想是二分。將集合s劃分成兩個子集s1和s2,它們的大小都是n/2。對於s1和s2,分別列舉出它們所有的2^(n/2)個子集和,儲存到某種支援查詢的資料結構中,例如hash set。

然後就要將兩部分結果合併,尋找是否有和為x的s的子集。事實上,對於s1的某個和為x1的子集,只需尋找s2是否有和為x-x1的子集。

假設採用的hash set是理想的,每次查詢和插入都僅花費o(1)的時間。兩步的時間複雜度顯然都是o(2^(n/2))。

實踐中,往往可以先將第一步得到的兩組子集和分別排序,然後再用兩個指標掃瞄的方法查詢是否有滿足要求的子集和。這樣的實現,在可接受的時間內可以解決的最大規模約為n=42。

在看到一道揹包問題時,應該用搜尋還是動態規劃呢?

首先,可以從資料範圍中得到命題人意圖的線索。如果乙個揹包問題可以用dp解,v一定不能很大,否則o(vn)的演算法無法承受,而一般的搜尋解法都是僅與n有關,與v無關的。所以,v很大時(例如上百萬),命題人的意圖就應該是考察搜尋。另一方面,n較大時(例如上百),命題人的意圖就很有可能是考 察動態規劃了。

另外,當想不出合適的動態規劃演算法時,就只能用搜尋了。例如看到乙個從未見過的揹包中物品的限制條件,無法想出dp的方程,只好寫搜尋以謀求一定的分數了。

記憶化搜尋解決揹包問題

為什麼要用二維陣列進行紀錄?記憶化最麻煩的還是記憶值的準確性 include include using namespace std struct noded 10000 long long n,m long long dp 1000 1000 一開始我直接使用的dp 1000000 以為將每個m對...

深度優先搜尋解決簡單揹包問題(遞迴)

hh還是 演算法筆記 中的專題,在271頁。問題 有n件物品已知每件的重量w i 價值c i 現在需要選出若干物品放入乙個容積為v的揹包中,使得選入揹包的物品重量和不超過v的前提下,讓揹包中的物品價值和最大,求最大價值 1 n 20 這裡採用的思想是深度優先搜尋,實現方法是遞迴法。遞迴式是對每個物品...

揹包問題的幾種解法及變形

揹包問題 一 01揹包 問題描述 給定n種物品和乙個揹包,物品i的重量是wi,價值是vi,揹包容量為w 對於每個物品,要麼裝揹包,要麼不裝 選擇裝揹包的物品集合,使得物品總重量不超過揹包容量w,且價值和盡量大 限制條件 1 n 100 1 wi,vi 100 1 w 10000 輸入 4 52 3 ...