動態規劃之01背包詳解
先看問題:
有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;我用紅色的數字標記一下在當前代碼
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背包詳解