有
n 件物品和乙個容量為
v的揹包.放入第
i 件物品.放入第
i件物品耗費的容量是ci
,所獲得的價值是wi
.每件物品只有乙個.求將哪些物品放入揹包可使價值總和最大.
一般來說求極值的問題可分為貪心,動態規劃,以及遍歷所有可能.在這三中方法中,動態規劃是最常見的,也是很難想出來的.其中最難的是定義子問題,寫出動態轉移方程.類似於將問題簡化,如何由較小的子問題,推到出較複雜的問題.類似的計算機的本質是極其簡單的二進位制運算,但由無數二進位制運算疊加在一起,組成了今天覆雜的網際網路系統.
在上次面試中,由於沒想出思路,很緊張,之後寫最簡單的01揹包也沒有寫出來.面試後,痛定思痛,之後要逐步加強的自己的演算法思維能力,證明問題能力,找錯能力,工具應用能力.
一般可從最簡單入手:用f
[i,v
] 表示前
i 件物品放入乙個容量為
v的揹包所獲得的最大價值.
在放與不放兩個子問題中選擇乙個最大的解 f[
i,v]
=max
(f[i
−1,v
],f[
i−1,
v−ci
]+wi
)
f[0, 0...v] = 0
for i = 1 to n
for j = 0 to v
if(c[i] > j)
f[i, j] = f[i - 1, j]
else
f[i, j] = max(f[i - 1, j], f[i - 1, j - c[i]] + w[i])
時間複雜度已無法再優化,. t=
o(nv
) 這裡使用的是二維資料,但求解第
i 行值時,只利用到i−
1行的解,並未利用更久之前的解,由於有這種侷限解的特性,可以利用一維的滾動陣列模擬二維資料,另外由於這裡由i−
1 的前面的解j−
c[i]
推導出i 後面的解
j,也就是利用一維的資料的話,舊解為前面的解,新解為後面的.那麼就應該讓
j 從大到小進行迴圈遍歷,因為這樣第一次接觸的到為舊解i−
1,新出來的新解
j 在此次遍歷也不會再用到.也就是當且僅使用了一次.如果是從小到大遍歷
j,那麼新求出的解j+
c[i]
在後面還會遍歷到,因此還會被重新用到,再次用到的話,還會選擇是否拿下這個物體,不符合題意每件物品只有乙個(如果有無窮多個物品,就可以從小到大遍歷,之後的完全揹包問題會提到)s=
o(v)
f[0...v] = 0
for i = 1 to n
for j = v to c[i]
f[j] = max(f[j], f[j - c[i]] + w[i]
**更將簡單優雅
如果是求恰好要裝滿揹包時的最優解,那麼像上面的都初始化為0就不行了.這時候需要只有f[0, 0] = 0, f[0, 1…v] = int_min(表示負無窮).可以理解只有0個物品,揹包為0容量正好有解,其餘解為無效解.如果手工畫出來子問題轉化圖示,這樣最基礎的子問題只能從f[
0,0]
出發,不會從其他無效子問題出發,因為初始化無效解是負無窮,兩者去較大的話,肯定不會選取負無窮.
可將偽**1再次優化為
for i = 1
to n
for j = v to
max(v - sum(c[i...n]), c[i])
f[j] = max(f[j], f[j - c[i]] + w[i])
由於只需要最後f[v]的值,倒推前乙個物品,其實只要知道f[v-c[n]]即可。以此類推,對以第j個揹包,其實只需要知道到f[v-sum]即可
參照一般動態規劃問題輸出方案的方法:記錄下每個狀態的最優質是由狀態轉移方程的哪一項得到的,也就是記錄最優解時,是否選擇了這個物品.具體來說是利用path[i, j] 來記錄當前的轉移過程,path[i, j] = true表示選取物品i, path = false 表示未選取當前物品.(bool陣列更加節省記憶體).空間複雜度s=
o(vn
) .
void zeroonepack(int c, int w, int v, vector
& f)
int allzeroonepack(vector
& c, vector
& w, int v)
void zeroonepack(int i, int c, int w, int v, vector
& f, vector
> &path)
}}int allzeroonepackdetail(vector
& c, vector
& w, int v)
return f[v];
}void printmethod(vector
& c)
i--;
}}
在利用dfs遞迴求解時,先將物品按照單位**排好序,單位**高的靠前,這樣如果某個物品超載時,沒必要再累積其後面物品的**, 而是按照該物品的單位**乘以剩餘容量,這樣算出的總**雖然比實際裝載的總**略高些,如果這樣略高於實際值的解還低於當前的最優解,則可對後面剪枝,避免多餘的計算.
揹包九講
0-1揹包問題(回溯結點類排序改進)
01揹包問題:charm bracelet (poj 3624)(外加乙個常數的優化)
揹包九講 01揹包問題
1 01揹包問題描述 已知 有 n 件物品和乙個容量為 v 的揹包。第i件物品的重量為w i 得到的價值是 c i 問題 求解將哪些物品裝入揹包可使價值總和最大。條件 每種物品只有一件,可以選擇放或者不放 2 基本思路 01揹包的特點 每種物品只有一件,可以選擇放或者不放 子問題定義狀態f i v ...
揹包問題模板(揹包九講)
題目 有 n 件物品和乙個容量為 v 的揹包。第 i 件物品的體積 費用 是 c i 價值是 w i 求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。如下 一維陣列 include include include using namespace std define m ...
揹包九講 01揹包問題(dp 滾動陣列)
現在有n件物品和乙個容量為v的揹包。第i件物品的費用是cost i 價值是value i 每個物品最多只能選一次,求解在不超過揹包容量的限制下,如何選取物品組合能使收益最大化?題型有兩種,一種要求揹包恰好放滿,一種不要求揹包恰好放滿 現在考慮第二種題型,即不要求揹包恰好放滿 將問題分解成子問題 有i...