現在有n件物品和乙個容量為v的揹包。第i件物品的費用是cost[i],價值是value[i]。每個物品最多只能選一次,求解在不超過揹包容量的限制下,如何選取物品組合能使收益最大化?題型有兩種,一種要求揹包恰好放滿,一種不要求揹包恰好放滿
現在考慮第二種題型,即不要求揹包恰好放滿
將問題分解成子問題:有i件物品,容量為j的揹包。
用dp[ i ][ j ]陣列儲存子問題的答案。
假設現在要求取dp[ i ] [ j ] ,我們考慮要不要選取第i件物品:
①如果不選取,則dp[ i ] [ j ]=dp[ i-1 ][ j ],因為dp陣列儲存的一直是當前狀態下最優的解,dp[ i-1 ][ j ]存放的值是在同樣容量為j的揹包限制下前i-1件物品最大的價值組合
②如果選取i件物品,考慮第i件物品的花費cost[ i ],則dp[ i ][ j ]=dp[ i-1] [ j-cost[i] ] +value [ i ] , 而 dp[ i-1] [ j-cost[i] ] 存放的值是容量為j-cost[ i ]限制下, 前i-1件物品最大的價值組合
綜上,得狀態轉移方程:
dp[ i ] [ j]=max(dp[ i-1 ][ j ], dp[ i-1] [j-cost[i] ] + value[ i ])
我們能保證不斷得到每乙個子問題的最優解,所以不斷遞推,即得到原問題的解
由之前的思路可得,解每乙個子問題dp[ i ][ j ]時,我們需要優先知道dp[ i-1 ][ j ]和dp[ i-1] [ j-cost[i] ] +value [ i ],觀察可得必須先知道dp陣列第i-1行的值
所有最外層迴圈讓i從小到大遞增
至於j是遞增還是遞減是無所謂的,因為求解子問題dp[ i ][ j ]時,我們只需要優先知道dp陣列第i-1行的值,至於第i行的求解順序是無所謂的
而考慮第一行,也就是只有一種物品時,那麼j是從小到大還是從大到小都無所謂,因為第一行的dp值只取決於j是否大於cost[1]
後面幾行都是第一行遞推而來,第一行沒問題後面幾行自然沒問題
int dp[10]
[2005];
memset
(dp,0,
sizeof
(dp));
for(
int i=
1;i<
8;i++
)for
(int j=n;j>=
1;j--
)
至於之前說的第一種題型,只要把dp陣列初始化為負無窮即可
負無窮表示沒有方案使該子問題得解
由之前的分析可以發現,求dp陣列第i行的值時,只需要用到dp陣列第i-1行的值,而其它幾行的值都是用不到的,所以為了節省空間,只需要用一維的dp陣列即可
但是要注意這其實是在用一維陣列來模擬二維陣列的情況,想象乙個不斷滾動的陣列
所以這裡的第二層迴圈中的j必須倒序了,不像之前是無所謂的了
原因很簡單,我們需要dp[ i-1] [ j-cost[ i ] ]的值,而如果j順序地從小到大增加則該值已經被覆蓋了,那麼該行後面的元素在計算時使用的就不是上一行的dp[ i-1] [ j-cost[ i ] ] ,其實已經被覆蓋為dp[ i] [ j-cost[ i ] ]
**:
int dp[
2005];
memset
(dp,0,
sizeof
(dp));
for(
int i=
1;i<
8;i++
)for
(int j=n;j>=
1;j--)if
(j>=cost[i]
) dp[j]
=max
(dp[j]
,dp[j-cost[i]
]+value[i]
);
同時也不需要else dp[i][j]=dp[i-1][j];
這句**了 ,因為不變相當於不去覆蓋上一行的dp值 揹包九講 01揹包問題
1 01揹包問題描述 已知 有 n 件物品和乙個容量為 v 的揹包。第i件物品的重量為w i 得到的價值是 c i 問題 求解將哪些物品裝入揹包可使價值總和最大。條件 每種物品只有一件,可以選擇放或者不放 2 基本思路 01揹包的特點 每種物品只有一件,可以選擇放或者不放 子問題定義狀態f i v ...
DP 揹包九講之01揹包
有n件物品和乙個容量為v 的揹包。放入第i件物品耗費的空間是ci,得到 的價值是wi。求解將哪些物品裝入揹包可使價值總和最大。這是最基礎的揹包問題,特點是 每種物品僅有一件,可以選擇放或不 放。用子問題定義狀態 即f i,v 表示前i件物品恰放入乙個容量為v的揹包可以 獲得的最大價值。則其狀態轉移方...
DP 揹包九講之01揹包
有 n 件物品和乙個容量是 v 的揹包。每件物品只能使用一次。第 i 件物品的體積是 vi,價值是 wi。求解將哪些物品裝入揹包,可使這些物品的總體積不超過揹包容量,且總價值最大。輸出最大價值。輸入格式 第一行兩個整數,n,v,用空格隔開,分別表示物品數量和揹包容積。接下來有 n 行,每行兩個整數 ...