1. 程式人生 > >動態規劃之完全背包詳解

動態規劃之完全背包詳解

現在 max 相同 維數 自己 一維數組 方法 table 得到

在昨天我已經很詳細的講解過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

動態規劃之完全背包詳解