在m件物品取出若干件放在空間為w的揹包裡,每件物品的體積為w1,w2……wn,與之相對應的價值為p1,p2……pn。求出獲得最大價值的方案。
注意:在本題中,所有的體積值均為整數。01的意思是,每個物品都是乙個整體,要麼整個都要,要麼都不要。
1)最優子結構
考慮所有物品的子集合,考慮第n個物品都有兩種情況: 1. 包括在最優方案中 2. 不在最優方案中
因此,能獲得的最大價值,即為以下兩個值中較大的那個
1) 在剩下 n-1 個物品中(剩餘 w 重量可用)的情況能得到的最大價值 (即排除了 第n個物品)
2) 第n個物品的價值 加上 剩下 剩下的 n-1 個物品(剩餘w- wn的重量)能得到的最大價值。(即包含了第n個物品)
如果第n個物品的重量,超過了當前的剩餘重量w,那麼只能選情況1), 排除第n個物品。
2) 重疊子問題
下面是乙個遞迴的實現,按照上面的最優子結構。
view source
01
/* 樸素的遞迴實現 0-1 揹包 */
02
#include
03
04
int
max(
int
a,
int
b)
05
06
// 返回 前n個物品在容量為w時,能得到的最大價值
07
int
knapsack(
int
w,
int
wt,
int
val,
int
n)
08
22
23
// 測試
24
int
main()
25
;
27
int
wt = ;
28
int
w = 50;
29
int
n =
sizeof
(val)/
sizeof
(val[0]);
30
printf
(
"%d"
, knapsack(w, wt, val, n));
31
return
0;
32
}
這種方法其實就是搜尋了所有的情況,但是有很多重複的計算。時間複雜度是指數級的 o(2^n)。
01
在下面的遞迴樹中 k() 代表 knapsack().
02
輸入資料如下:
03
wt = , w = 2, val =
04
05
k(3, 2) ---------> k(n, w)
06
/ \
07
/ \
08
k(2,2) k(2,1)
09
/ \ / \
10
/ \ / \
11
k(1,2) k(1,1) k(1,1) k(1,0)
12
/ \ / \ / \
13
/ \ / \ / \
14
k(0,2) k(0,1) k(0,1) k(0,0) k(0,1) k(0,0)
可見相同的子問題被計算多次。01揹包滿足動態規劃演算法的兩個基本屬性(重疊子問題和最優子結構)。可以通過自下而上的打表,儲存中間結果,來避免重複計算。動態規劃解法如下:
01
#include
02
int
max(
int
a,
int
b)
03
04
int
knapsack(
int
w,
int
wt,
int
val,
int
n)
05
20
}
21
return
dp[n][w];
22
}
23
24
int
main()
25
;
27
int
wt = ;
28
int
w = 50;
29
int
n =
sizeof
(val)/
sizeof
(val[0]);
30
printf
(
"%d"
, knapsack(w, wt, val, n));
31
return
0;
32
}
空間複雜度和時間複雜度都為 o(wn). 由於打表的過程中,計算的當前行只依賴上一行,空間複雜度可以優化為o(w);
參考:更多關於演算法分析的文章:
動態規劃揹包問題 01揹包
問題描述 n種物品,每種乙個。第i種物品的體積為vi,重量為wi。選一些物品裝到容量為c的揹包,使得揹包內物品不超過c的前提下,重量最大。問題分析 宣告乙個f n c 的陣列。f i j 表示把前i件物品都裝到容量為j的揹包所獲得的最大重量。當 j v i 時,揹包容量不足以放下第 i 件物品,f ...
動態規劃 揹包問題 01揹包
有n種物品和乙個容量為v的揹包,每種物品僅用一次。第i件物品的費用是w i 價值是v i 求解將哪些物品裝入揹包可使價值總和最大。例如 n 5,v 10 重量 價值 第乙個物品 10 5 第二個物品 1 4 第三個物品 2 3 第四個物品 3 2 第五個物品 4 1 首先我們考慮貪心策略,選取最大價...
0 1揹包問題(動態規劃)
一 問題描述 有n件物品和乙個容量為v的揹包。第i件物品的費用是c i 價值是w i 求解將哪些物品裝入揹包可使價值總和最大。所謂01揹包,表示每乙個物品只有乙個,要麼裝入,要麼不裝入。二 解決方案 考慮使用動態規劃求解,定義乙個遞迴式 opt i v 表示前i個物品,在揹包容量大小為v的情況下,最...