動態規劃之完全背包詳解
在昨天我已經很詳細的講解過01背包的動態規劃問題了,今天我講解的是完全背包的問題,這是01背包的詳解:http://www.cnblogs.com/Kalix/p/7617856.html
先看問題:在n種物品中選取若幹件(同一種物品可多次選取)放在空間為v的背包裏,每種物品的體積為c1,c2,…,cn,與之相對應的價值為w1,w2,…,wn.求解怎麽裝物品可使背包裏物品總價值最大
看完這個問題,你也許會覺得這個不就是01背包的升級版嗎,其實就是這樣,完全背包問題與01背包問題的區別在於完全背包每一件物品的數量都有無限個,而01背包每件物品數量只有1個
所以說與它相關的策略已經不是只有取和不取這兩種策略了,而是有取0件、取1件、取2件……等等很多種策略
如果我們用和01背包一樣的狀態,f[i][v]表示前i種物品恰放入一個容量為v的背包的最大價值,那我們應該用k表示當前容量下可以裝第i種物品的件數,那麽k的範圍應該是0≤k≤v/c[i],
既然要用當前物品i把當前容量裝滿,那需要0≤k≤v/c[i]件,其中k表示件數。
下面給出狀態轉移方程:
f[i][j] = max{f[i-1][v],f[i-1][v - k * c[i]] + k * w[i]}(0<=k*c[i]<=v)
貼一段代碼:
for (int i = 1; i < n; i++){ for (int j = 1; j <= v; j++){ for (int k = 0; k*c[i] <= j; k++){ if(c[i]<=j)/*如果能放下*/ f[i][j] = max{f[i-1][j],f[i-1][j - k * c[i]] + k * w[i]};/*要麽不取,要麽取0件、取1件、取2件……取k件*/ else/*放不下的話*/ f[i][j]=f[i-1][j]/*繼承前i個物品在當前空間大小時的價值*/ } } }
我們可以對其進行優化:如果有兩件物品a、b滿足c[a]<=c[b]且w[a]>=w[b],則將物品b去掉,不用考慮。因為你可以用 占用體積小的物品 得到 比 占用體積大的物品還要多的價值,何樂而不為呢。其實對於完全背包,可以再優化,首先將容量大於v的物品去掉,然後排序計算出容量相同的物品中價值最高的是哪個,我們只要價值大的就可以了。
畫一個v=6,c[1]=1 , w[1]=3 ; c[2]=3 , w[2]=10的表格
i\j |
j=0 | j=1 | j=2 | j=3 | j=4 | j=5 | j=6 | ||
i=0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | ||
i=1 |
0 | 3 | 6 | 9 | 12 | 15 | 18 | ||
i=2 |
0 | 3 | 6 | 10 | 13 | 16 | 20 |
我們再進行優化,改變一下dp思路,讓f[i][j]表示出在前i種物品中選取若幹件物品放入容量為j的背包所得的最大價值。
所以說,對於第i件物品有放或不放兩種情況,而放的情況裏又分為放1件、2件、......v/c[i]件
如果不放那麽f[i][j]=f[i-1][j];如果確定放,那麽當前背包中應該出現至少一件第i種物品,所以f[i][j]中至少應該出現一件第i種物品,即f[i][j]=f[i][j-c[i]]+w[i],為什麽會是f[i][j-c[i]]+w[i]?
因為我們要把當前物品i放入包內,因為物品i可以無限使用,所以要用f[i][j-c[i]];如果我們用的是f[i-1][j-c[i]],f[i-1][j-c[i]]的意思是說,我們只有一件當前物品i,所以我們在放入物品i的時候需要考慮到第i-1個物品的價值(f[i-1][j-c[i]]);但是現在我們有無限件當前物品i,我們不用再考慮第i-1個物品了,我們所要考慮的是在當前容量下是否再裝入一個物品i,而[j-c[i]]的意思是指要確保f[i][j]至少有一件第i件物品,所以要預留c[i]的空間來存放一件第i種物品。總而言之,如果放當前物品i的話,它的狀態就是它自己"i",而不是上一個"i-1"。
所以說狀態轉移方程為:
f[i][j]=max(f[i-1][j],f[i][j-c[i]]+w[i])
貼一段代碼:
for(int i = 0 ; i < n ; i ++)
{
for(int j = 1 ; j <= v ; j++)
{
if(c[i]<=j)
f[i][j] = max(f[i-1][j],f[i][j-c[i]]+w[i]);
else
f[i][j]=f[i-1][j];
}
}
我們可以繼續優化此算法,可以用一維數組寫
我們用f[j]表示當前可用體積j的價值,我們可以得到和01背包一樣的遞推式:
f[j] = max(f[j],f[j-c[i]]+w[i])
先貼出代碼,再講解:
for(int i = 0 ; i < N ; i ++) { for(int j = c[i] ; j <= V ; j++) { if(c[i]<j) f[j] = max(f[j],f[j-c[i]]+w[i]); else f[j]=f[j];
} }
對於01背包來說,是逆序,裝不裝當前物品i取決於第i-1個物品(f[i][]只和f[i-1][]有關);而對於完全背包來說,裝不裝物品取決於他前一個物品i(因為可以放無數個物品i),因為當前物品i是無限的不用去考慮f[i-1][],而應當考慮當前狀態下是否應當再裝入當前一個物品i。或者換一種說法:完全背包考慮的是第i種物品的出現的問題(該不該放入當前物品i),第i種物品一旦出現它勢必應該對第i種物品還沒出現的各狀態造成影響,也就是說,原來沒有第i種物品的情況下可能有一個最優解,現在第i種物品出現了,而它的加入有可能得到更優解,所以之前的狀態需要進行改變,故需要正序。
也就是說完全背包是說,在當前體積下,是否要放入或者再放入一個當前物品i,而01背包是說,在當前體積下,是否要放入一個當前物品i
對於完全背包還可以用二進制的方法寫,這裏就不講了
謝謝
本文章原創,未經我允許不得轉載
Authentic Author : Tranx
2017.10.2 19:18
動態規劃之完全背包詳解