背包問題九講筆記_01背包
背包問題是動態規劃中最基本的題目。
動態規劃的4步驟:
1.找出子結構
2.遞歸
3.自底而上
4.求最優解
01背包問題描述
已知:有一個容量為V的背包和N件物品,第i件物品的重量是weight[i],收益是cost[i]。
限制:每種物品只有一件,可以選擇放或者不放
問題:在不超過背包容量的情況下,最多能獲得多少價值或收益
相似問題:在恰好裝滿背包的情況下,最多能獲得多少價值或收益
這里,我們先討論在不超過背包容量的情況下,最多能獲得多少價值或收益。
基本思路
01背包的特點:每種物品只有一件,可以選擇放或者不放
子問題分析:
f[i][v] : 前i件物品放到一個容量為v的背包中可以獲得最大價值
狀態轉移方程:
f[i][v] = max(f[i - 1][v],f[i - 1][v - weight[i]] + cost[i])
分析
考慮我們的子問題,將前i件物品放到容量為v的背包中,若我們只考慮第i件物品時,它有兩種選擇,放或者不放。
- 如果第i件物品不放入背包中,那么問題就轉換為:將前i - 1件物品放到容量為v的背包中,帶來的收益f[i - 1][v]
- 如果第i件物品能放入背包中,那么問題就轉換為:將前i - 1件物品放到容量為v - weight[i]的背包中,帶來的收益f[i - 1][v - weight[i]] + cost[i]
核心代碼:
for(i = 1; i < n ; i ++)
for( j = 1; j < V ; j ++)
{
pd[i][j] = pd[i-1][j];
if( weight[i] <= j )
pd[i][j] = max(pd[i - 1][j],pd[i - 1][j - weight[i]] + cost[i]) ;
}
效率分析:
此算法的時間復雜度為O(NV),空間復雜度也為O(NV)。其中,N 表示物品個數,V 表示背包容量這里,時間復雜度不可以在優化了,但是空間復雜度可以繼續優化到O(V)
優化空間復雜度
上述的方法,我們使用二維數組 f[i][v] 保存中間狀態,這里我們可以使用一維數組f[v]保存中間狀態就能得到結果
分析
我們現在使用f[v]保存中間狀態,我們想要達到的效果是,第i次循環后,f[v]中存儲的是前i個物體放到容量v時的最大價值,即對應二維數組的f[i][v]
根據上面的狀態轉移方程,
f[i][v] = max(f[i - 1][v],f[i - 1][v - weight[i]] + cost[i])
可以知道,要想得到f[v],前i - 1個物品放到容量v的背包中帶來的收益,即之前的f[i - 1][v] 和 前i - 1件物品放到容量為v - weight[i]的背包中帶來的收益,即之前的f[i - 1][v - weight[i]] + cost[i]。
在一維數組f[v]中,在執行在i次循環時,f[v]存儲的是前i個物體放到容量v時的最大價值。即此時的f[v]相當于之前的f[i-1][v]正是我們需要知道的第一項。
而f[v-weight[i]]+cost[i]就是需要求的第二項。
求f[v]還好,此時的f[v]就是f[i-1][v]。但是如果依舊是增序遍歷容量,那么當處理f[v]的時候 f[v-weight[i]]已經被處理過,此時f[v-weight[i]]的意義就變成前i個物品放到容量v-weight[i]背包中的收益了,那么就直接失去了f[i - 1][v - weight[i]]。
因此需要 逆序遍歷容量 (V....0)才能做到 在第i次循環中,處理f[v]的時候,
f[v-weight[i]]還是f[i-1][v-weight[i]]的意義
偽代碼:
for i=1..N //枚舉物品
for v=V..0 //枚舉容量,從大到小
f[v]=max{f[v],f[v-weight[i]] + cost[i]};