01揹包問題,是用來介紹動態規劃演算法最經典的例子,網上關於01揹包問題的講解也很多,我寫這篇文章力爭做到用最簡單的方式,最少的公式把01揹包問題講解透徹。
f[i,j]表示在前i件物品中選擇若干件放在承重為 j 的揹包中,可以取得的最大價值。
pi表示第i件物品的價值。
決策:為了揹包中物品總價值最大化,第 i件物品應該放入揹包中嗎 ?
題目描述:
有編號分別為a,b,c,d,e的五件物品,它們的重量分別是2,2,6,5,4,它們的價值分別是6,3,5,4,6,現在給你個承重為10的揹包,如何讓揹包裡裝入的物品具有最大的價值總和?
name
weight
value12
3456
78910
a260
6699
1212
151515b
2303
3669
991011c6
5000
6666
61011d
5400
0666
661010e4
6000
6666
666
只要你能通過找規律手工填寫出上面這張表就算理解了01揹包的動態規劃演算法。
首先要明確這張表是至底向上,從左到右生成的。
為了敘述方便,用e2單元格表示e行2列的單元格,這個單元格的意義是用來表示只有物品e時,有個承重為2的揹包,那麼這個揹包的最大價值是0,因為e物品的重量是4,揹包裝不了。
對於d2單元格,表示只有物品e,d時,承重為2的揹包,所能裝入的最大價值,仍然是0,因為物品e,d都不是這個揹包能裝的。
同理,c2=0,b2=3,a2=6。
對於承重為8的揹包,a8=15,是怎麼得出的呢?
根據01揹包的狀態轉換方程,需要考察兩個值,
乙個是f[i-1,j],對於這個例子來說就是b8的值9,另乙個是f[i-1,j-wi]+pi;
在這裡,
f[i-1,j]表示我有乙個承重為8的揹包,當只有物品b,c,d,e四件可選時,這個揹包能裝入的最大價值
f[i-1,j-wi]表示我有乙個承重為6的揹包(等於當前揹包承重減去物品a的重量),當只有物品b,c,d,e四件可選時,這個揹包能裝入的最大價值
f[i-1,j-wi]就是指單元格b6,值為9,pi指的是a物品的價值,即6
由於f[i-1,j-wi]+pi = 9 + 6 = 15 大於f[i-1,j] = 9,所以物品a應該放入承重為8的揹包
01揹包問題:乙個揹包總容量為v,現在有n個物品,第i個 物品體積為weight[i],價值為value[i],現在往揹包裡面裝東西,怎麼裝能使揹包的內物品價值最大?
看到這個問題,可能會想到貪心演算法,但是貪心其實是不對的。例如最少硬幣找零問題,要用動態規劃。動態規劃思想就是解決子問題並記錄子問題的解,這樣就不用重複解決子問題了。
動態規劃先找出子問題,我們可以這樣考慮:在物品比較少,揹包容量比較小時怎麼解決?用乙個陣列f[i][j]表示,在只有i個物品,容量為j的情況下揹包問題的最優解,那麼當物品種類變大為i+1時,最優解是什麼?第i+1個物品可以選擇放進揹包或者不放進揹包(這也就是0和1),假設放進揹包(前提是放得下),那麼f[i+1][j]=f[i][j-weight[i+1]+value[i+1];如果不放進揹包,那麼f[i+1][j]=f[i][j]。
這就得出了狀態轉移方程:
f[i+1][j]=max(f[i][j],f[i][j-weight[i+1]+value[i+1])。
可以寫出**測試:
#includeusing namespace std;
#define v 1500
unsigned int f[10][v];//全域性變數,自動初始化為0
unsigned int weight[10];
unsigned int value[10];
#define max(x,y) (x)>(y)?(x):(y)
int main()
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++)
else
f[i][j]=f[i-1][j];
} cout《在hihocoder上面還講到可以進一步優化記憶體使用。上面計算f[i][j]可以看出,在計算f[i][j]時只使用了f[i-1][0……j],沒有使用其他子問題,因此在儲存子問題的解時,只儲存f[i-1]子問題的解即可。這樣可以用兩個一維陣列解決,乙個儲存子問題,乙個儲存正在解決的子問題。
再進一步思考,計算f[i][j]時只使用了f[i-1][0……j],沒有使用f[i-1][j+1]這樣的話,我們先計算j的迴圈時,讓j=m……1,只使用乙個一維陣列即可。
for i=1……n
for j=m……1
f[j]=max(f[j],f[j-weight[i]+value[i])
#includeusing namespace std;
#define v 1500
unsigned int f[v];//全域性變數,自動初始化為0
unsigned int weight[10];
unsigned int value[10];
#define max(x,y) (x)>(y)?(x):(y)
int main()
for (int i=1; i<=n; i++)
for (int j=m; j>=1; j--)
} cout<
在看完01揹包問題,再來看完全揹包問題:
乙個揹包總容量為v,現在有n個物品,第i個 物品體積為weight[i],價值為value[i],每個物品都有無限多件,現在往揹包裡面裝東西,怎麼裝能使揹包的內物品價值最大?
對比一下,看到的區別是,完全揹包問題中,物品有無限多件。往揹包裡面新增物品時,只要當前揹包沒裝滿,可以一直新增。那麼狀態轉移方程為:
f[i+1][j]=max(f[i][j-k*weight[i+1]+k*value[i+1]),其中0<=k<=v/weight[i+1]
使用記憶體為一維陣列,偽**
for i=1……n
for j=1……m
f[j]=max(f[j],f[j-weight[i]+value[i])
和01揹包問題唯一不同的是j是從1到m。01揹包問題是在前乙個子問題(i-1
種物品)的基礎上來解決當前問題(i
種物品),向i-1種物品時的揹包新增第i種物品;而完全揹包問題是在解決當前問題(i種物品),向i種物品時的揹包新增第i種物品。
**如下:
#includeusing namespace std;
#define v 1500
unsigned int f[v];//全域性變數,自動初始化為0
unsigned int weight[10];
unsigned int value[10];
#define max(x,y) (x)>(y)?(x):(y)
int main()
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++)
} cout<
01揹包問題和完全揹包問題
在hihocoder上面的題目中看到的這個問題,總結一下。先看01揹包問題。01揹包問題 乙個揹包總容量為v,現在有n個物品,第i個 物品體積為weight i 價值為value i 現在往揹包裡面裝東西,怎麼裝能使揹包的內物品價值最大?看到這個問題,可能會想到貪心演算法,但是貪心其實是不對的。例如...
01揹包問題和完全揹包問題
時間限制 20000ms 單點時限 1000ms 記憶體限制 256mb 描述且說上一周的故事裡,小hi和小ho費勁心思終於拿到了茫茫多的獎券!而現在,終於到了小ho領取獎勵的時刻了!小ho現在手上有m張獎券,而獎品區有n件獎品,分別標號為1到n,其中第i件獎品需要need i 張獎券進行兌換,同時...
01揹包問題和完全揹包問題
在hihocoder上面的題目中看到的這個問題,總結一下。先看01揹包問題。01揹包問題 乙個揹包總容量為v,現在有n個物品,第i個 物品體積為weight i 價值為value i 現在往揹包裡面裝東西,怎麼裝能使揹包的內物品價值最大?看到這個問題,可能會想到貪心演算法,但是貪心其實是不對的。例如...