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

動態規劃之01背包詳解

題解 for 可見 round 往裏面 原創 ble -a eight

先看問題:

有N件物品和一個容量為V的背包。(每種物品均只有一件)第i件物品的費用是c[i],價值是w[i]。求解將哪些物品裝入背包可使價值總和最大。

通過閱讀問題,因為背包就是要往裏面放東西,所以一件物品就是有放或不放兩種情況,那麽怎麽才能判斷當前物品該不該放進去從而使利益最大化呢。

首先,我們用i代表前i件物品,v代表包的最大承重,ci是第i件物品的重量、wi是第i件物品的價值、f[i,j]是最大價值(i個物品放入有j個空間的包)。

第一種情況:第i件不放進去,這時所得價值(f[i][j])為:f[i][j]=f[i-1][v]

因為不放進去,所以說當前價值(f[i][j])為前i-1個物品的價值f[i-1][v]:當前i個物品用j個空間所擁有的價值就等於前i-1個物品用j個空間的價值,因為物品i沒有放進去,

所以就相當於繼承f[i-1][v]了,所以f[i][j]=f[i-1][v]

另一種情況:第i件放進去,這時所得價值為:f[i][j]=f[i-1][v-c[i]]+w[i]

因為放進去了,所以說當前價值(f[i][j])不是前i-1個物品的價值f[i-1][v]:因為你放進去了,所以這個時候包內的空間就不是v了,而應該是v-c[i],所以說,

你現在需要繼承前i-1個物品占用體積為v-c[i]時的價值,因為你將物品放進去了,所以還需要加上當前物品價值wi,所以f[i][j]=f[i-1][v-c[i]]+w[i]

因為對於第i件物品就是這兩種操作,而你又想要最大價值,所以

f[i][j]=max(f[i-1][v],f[i-1][v-c[i]]+w[i])

貼一段代碼:(n為物品數量)

for (int i=1;i<=n;++i)
{ for (int j=v;j>=1;--j)
   { if(c[i]<=j)//如果當前物品可以放入當前空間的背包 f[i][j]=max(f[i-1][j],f[i-1][j-c[i]]+w[i]); else f[i][j]=f[i-1][j];//如果當前物品放不進去,那麽繼承前i個物品在當前空間大小時的價值    } }

當n=3,v=6時的表格:

c[1]=2 , w[1]=7 ; c[2]=3 , w[2]=1 ; c[3]=5 , w[3]=4;我用紅色的數字標記一下在當前代碼

下填表的順序,運行之後f[n][v]為最優值,可見此時最優值為f[3][6]=8;

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 (6)7 (5)7 (4)7 (3)7 (2)7 (1)7

i=2

0 (12)8 (11)8 (10)8 (9)8 (8)8 (7)8

i=3

0 (18)0 (17)0 (16)0 (15)0 (14)8 (13)8

以上是用二維數組存儲的,其實還可以用一維數組存儲進行空間優化(滾動數組)

從上面計算f[i][j]可以看出,在計算f[i][j]時只使用了f[i-1][0……j],所以說並沒有使用其他子問題,所以說在存儲子問題解的時候,只用存儲f[i-1]的子問題解即可;所以說可以用一個一維數組替換掉那個二維數組,一個存儲子問題,一個存儲正在解決的子問題。

我們用f[v]表示當前狀態是容量為v的背包所得價值

那滾動數組應當如何滾動呢,用二維數組計算f[i][j]時只使用了f[i-1][0……j],而並沒有使用到f[i-1][j+1],所以在計算j的循環的時候,讓j=M……1

這個時候你也許會問,那你上面用的二維數組不也是逆序嗎,其實對於二維數組的來說,正逆序無所謂,當你懂得01背包的算法之後就可以明白這一點

下面給出一維數組優化過的代碼:

for (int i=1;i<=n;++i)
{
        for (int j=v;j>=0;--j)
    {
            if(t[i]<=j)
            f[j]=max(f[j],f[j-t[i]]+t[i]);
            else f[j]=f[j];
     }
}

  因為二維的我已經講解的很詳細了,所以一維數組的跟二維數組的相差的不是太多,只要你懂了二維數組的那個是如何工作的,這個我相信你也會很容易理解,我這裏就不細化講解了。

本文章原創,未經我允許不得轉載

Authentic Author : Tranx

2017.10.1 21:11

動態規劃之01背包詳解