0 1揹包問題

2021-06-14 21:18:18 字數 2536 閱讀 7147

0-1揹包問題是經典的動態規劃的例子,個人覺得這是個非常好的問題,可以用許多方法來解決,而且思路也各有不同。

個人比較推薦這三個部落格:  將動態規劃問題講解得比較詳細。但是裡面也難免有些地方不是很清楚。這裡我也講講我的看法,順便對第二篇文章做點補充。

先定義0-1揹包問題,注意這裡的符號與下面解題的前後一致:

有n件物品和乙個容量為w的揹包, 

每種物品均只有一件。

第i件物品的重量是w[i],價值是v[i]。求解將哪些物品裝入揹包可使價值總和最大。

來乙個例子:

有3件物品,揹包容量為10,物品重量的陣列為w=, 價值的陣列為v=

例子的好處是可以隨時驗證我們的想法是否正確

如果一開始不告訴你如何來解0-1揹包問題,第乙個冒出來的解題思路是什麼?

盡可能的放價值大而重量小的物品,按照r[i]=v[i]/w[i]從大到小裝物品。等等,用例子驗證一下,這裡r=,也就是應該先裝第乙個,再裝第二個,第三個裝不下了,於是,得到的最大價值為4+5=9。……直觀上看去,5+6=11才是正解,那麼,這種方式無法解決這個問題。好吧,我承認,剛剛耍了你,只不過這個是思維的一種方式。這種方式稱為貪心法貪心法適合解決分數揹包問題(即物品可以切割),適用貪心法解決的問題應當有最優子結構以及貪心選擇性質。所謂的貪心選擇性質,就是每一步都只需要考慮當前問題,而不需要考慮子問題(所以剛剛的想法是第乙個冒出來的,因為非常簡單,每次都只需要考慮當前的價值/重量最大的物品)。

接下來想的可能就是最簡單暴力能夠實現的方式,即窮舉法乙個乙個列舉所有能進入揹包的可能性,將只取1個,到只取2個,到只取n個的所有可能算出來,由於這裡3個會撐爆揹包,所以下圖未列出3個的組合,計算完畢之後按照價值倒序排列就是想要的結果。可以看到,這裡的計算次數是c(3,1) + c(3,2),寫成一般形式就是c(n,1)+c(n,2)+...+c(n,n),而這個

結果是1+2+2²+……2^n-1,雖然有可能最後一些結果可以被剪掉(比如這裡的3-3),但是可以想象,當n增大時無論時間空間都是不允許的。

好吧,想想看這裡有什麼問題?

這個時候,應當想到,如何將原問題分解為子問題,這是一種比較常用的思想,運用這種思想的兩大類演算法就是分治法和動態規劃,因此非常的牛x。可以想到的一種分解子問題的方式是:使用當前物品後重量為w的解空間不使用當前物品重量就為w的解空間中較大的乙個。於是,依據這種子結構,可以將問題用遞迴實現出來,這裡簡單寫了乙個。

public class pack  else

} else else}}

public static void main(string args) ;

int value = ;

int maxw = 10;

system. out.println(packdp(0, maxw, 0, weight, value));}}

可以將這個遞迴過程用圖簡單畫出來,以上面例子為例,最初是求1,2,3這個問題域,但是第一次劃分就將問題化為兩個子問題,左分支是物品1在解裡達到10的重量的情況,右分支是物品1不在解裡面達到10的重量的情況,這兩種情況取大者就是問題的實際價值。線段上的1表示物品編號,3表示物品1的重量,7表示除去物品1當前剩餘重量。而左分支選擇以後的實際價值就變成物品1的價值+packdp(2,3)這個子問題的價值,即4+packdp(2,3)。

仔細分析一下,上面的解法有什麼問題?似乎求解的時候重複了一些子問題,也就是說子問題重疊了(使用遞迴求解斐波那契數列時這個問題特別明顯)。

使用動態規劃的兩個特徵就是最優子結構以及子問題重疊。而動態規劃的精髓就在於使用了額外的儲存儲存了更小的子問題。這種儲存有兩種方式。

一是自頂向下,典型的就如上面的遞迴,如果要使用遞迴來實現同樣的問題,則必須使用備忘機制來儲存中間的子問題結果(例如乙個hashtable),以便下次計算時直接取得。

二是自底向上,典型的方式就是迭代,迭代通常使用陣列一步一步的向上儲存子問題的解,上層每一步都會使用已經計算出來的子問題解。

下面是動態規劃迭代法實現的過程,f[r]表示將前i件物品放入容量為w的揹包時的價值,而到底放入第幾件是由迴圈次數i來決定的。這裡,我也推薦手動計算一下以便理解這個過程。有不理解可以參考第二篇部落格

for i=1..n

for r=w..0

f[r]=max;

實際上,後面還可以用回溯法跟分支限界法來解決,可以參考推薦的第三篇部落格。

最後了,想起乙個比較有趣的題目:有n級台階,每次可以跳1級,2級……直到n級,那麼跳這n級台階一共有幾種跳法?

這個題比較有趣,借助於這個問題,可以幫我們梳理下動態規劃法的精髓。

參考:

揹包問題 01揹包問題

n個物品,總體積是v,每個物品的體積的vi,每個物品的最大價值是wi,在不超過v的體積下求最大價值 eg揹包容積為 5 物品數量為 4 物品的體積分別為 物品的價值分別為 思路定義乙個二位陣列int f new int n 1 v 1 f i j 就表示在1 i個物品中選取體積小於v的情況的最大價值...

揹包問題 01揹包

有n件物品和乙個容量為v的揹包。第i件物品的重量是c i 價值是w i 求解將哪些物品裝入揹包可使價值總和最大。01揹包中的 01 就是一種物品只有1件,你可以選擇放進去揹包即1,也可以選擇不放入揹包中即0。include include using namespace std const int ...

揹包問題(01揹包)

1085 揹包問題 在n件物品取出若干件放在容量為w的揹包裡,每件物品的體積為w1,w2 wn wi為整數 與之相對應的價值為p1,p2 pn pi為整數 求揹包能夠容納的最大價值。input 第1行,2個整數,n和w中間用空格隔開。n為物品的數量,w為揹包的容量。1 n 100,1 w 10000...