01揹包、完全揹包、多重揹包詳解
目錄
一、01揹包問題
問題:
有n件物品和一個容量是m的揹包。第i件物品的體積是vi,價值是wi。
求解將哪些物品裝入揹包,可使這些物品的總體積不超過揹包容量,且總價值最大。輸出最大價值。
解答:
問題中每個物品只有選與不選兩種狀態。
動態規劃的狀態轉移方程:dp[i][j] = max(dp[i-1][j], dp[i][j-vi]+wi) , d[i][j]表示只看前i個物品且總體積是j時,最大的價值。
//v是物品的體積,w是價值,m是揹包容量
int package(vector<int> &v, vector<int> &w, int m) {
int n = v.size();
vector<vector<int>> dp(n+1, vector<int>(m+1,0));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
dp[i][j] = dp[i - 1][j];
if(j >= v[i-1])
dp[i][j] = max(dp[i][j], dp[i-1][j - v[i-1]] + w[i-1]);
}
}
return dp[n][m];
}
程式碼分析:
首先,最外層遍歷物品(1~n)。內層遍歷揹包容量(1~m)。在實現dp[i][j] = max(dp[i-1][j], dp[i][j-vi]+wi)狀態轉移時,在計算dp[i][j]時,需要使用dp[i-1][j]的內容。觀察程式碼可以發現每次的狀態轉移只與上一次的結果有關,會使dp[i-1][j] 與dp[i-1][j-vi]。
我們反向使用上一次的結果可以簡化輔助空間得到下面的狀態轉移:dp[j] = max(dp[j], dp[j-vi]+wi)
int package(vector<int> &v, vector<int> &w, int m) {
int n = v.size();
vector<int> dp(m + 1, 0);
for (int i = 0; i < n; i++) {
for (int j = m; j >= v[i]; j--) {
dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
}
}
return dp[m];
}
二、完全揹包問題
問題:
有n種物品,每種物品可以選無數次和0次,揹包的容量是m。第i件物品的體積是vi,價值是wi。
求:將哪些物品裝入揹包,可使這些物品的總體積不超過揹包容量,且總價值最大。輸出最大價值。
分析:
問題中每個物品擁有選擇多個和不選的狀態。
動態規劃的狀態轉移方程與01揹包相同:dp[j] = max(dp[j], dp[j-vi]+wi) , d[j]表示總體積是j時,最大的價值。
int package(vector<int> &v, vector<int> &w, int m) {
int n = v.size();
vector<int> dp(m + 1, 0);
for (int i = 0; i < n; i++) {
for (int j = v[i]; j <= m; j++) {
dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
}
}
return dp[m];
}
與01揹包問題的程式碼略微有點不同,最外層同樣是遍歷物品(1~n)。內層遍歷從v[i]開始到m
三、多重揹包
問題:
有n種物品和一個容量是m的揹包。第i種物品的體積是vi,價值是wi,數量是si
求解將哪些物品裝入揹包,可使這些物品的總體積不超過揹包容量,且總價值最大。輸出最大價值。
可以把多重揹包看作是01揹包處理,使用與01揹包相同的方法。
int package(vector<int> &v, vector<int> &w, vector<int> &s, int m) {
int n = v.size();
vector<int> dp(m + 1, 0);
for (int i = 0; i < n; i++) {
for (int j = m; j >= v[i]; j--) {
for (int k = 1; k <= s[i] && k*v[i] <= j; k++) {
dp[j] = max(dp[j], dp[j - k*v[i]] + k*w[i]);
}
}
}
return dp[m];
}
程式碼分析:
與01揹包同樣的方法 ,只是把每一個物品的不同數量si當做是有si種物品進行處理,多了一層對同一物品的迴圈。
時間複雜度O(n3)
二進位制優化的方法
學過linux作業系統的同學應該知道,修改linux檔案的許可權的數字法。
如何使用最少的數的組合表示一個數的全域。比如:用一些數字或它們的組合來表示(1~7)所有的數。
0 0 0
4 2 1
在二進位制中,7 = 4+2+1,6=4+2,5=4+1,4=4,3=2+1,2=2,1=1
在上一個方法中,我們需要遍歷同一物品的所有的個數。複雜度很高,使用二進位制方法可以,有效降低時間複雜度。
比如有一種物品i有si件,如果si是7時。我們可以把這些物品拆分成4件,2件,1件。將4件物品的體積與價值分別相加當作一件新的物品加入物品集合,接下來2件,1件同理。我們就得到了一個每種物品只有1件的物品集合。可以用01揹包方法來解決。
int package_bit(vector<int> &v, vector<int> &w, vector<int> s, int m) {
int n = v.size();
vector<int> vr, wr;
for (int i = 0; i < n; i++) {
for (int k = 1; k <= s[i]; k *= 2) {
s[i] -= k;
vr.emplace_back(k*v[i]);
wr.emplace_back(k*w[i]);
}
if (s[i] > 0) {
vr.emplace_back(s[i] * v[i]);
wr.emplace_back(s[i] * w[i]);
}
}
vector<int> dp(m + 1, 0);
for (int i = 0; i < vr.size(); i++) {
for (int j = m; j >= vr[i]; j--) {
dp[j] = max(dp[j], dp[j - vr[i]] +wr[i]);
}
}
return dp[m];
}
相關文章
- 【模板】01揹包、完全揹包
- 揹包問題(01揹包與完全揹包)
- javascript演算法基礎之01揹包,完全揹包,多重揹包實現JavaScript演算法
- 揹包DP——完全揹包
- 01揹包和完全揹包問題解法模板
- 分組揹包、完全揹包
- 01揹包、有依賴的揹包
- 01 揹包
- hdu3591The trouble of Xiaoqian 多重揹包+全然揹包
- 揹包DP——混合揹包
- dp-完全揹包
- 從【零錢兌換】問題看01揹包和完全揹包問題
- 005多重揹包問題||
- 01揹包問題
- 01 揹包問題
- POJ 2184 (01揹包)
- 揹包
- 01揹包詳解第一版
- 01 揹包的變形
- 01揹包問題的解決
- 用各種方法解01揹包
- 揹包DP
- 【BZOJ 5003】與鏈 (多重揹包 dp)
- 深入剖析多重揹包問題(上篇)
- 深入剖析多重揹包問題(下篇)
- 2. 01揹包問題
- 【程式碼隨想錄】完全揹包
- 揹包問題
- 01揹包面試題系列(一)面試題
- ZOJ 3956——Course Selection System(01揹包)
- 你真的懂01揹包問題嗎?01揹包的這幾問你能答出來嗎?
- HDU Piggy-Bank(完全揹包問題)
- 通天之分組揹包
- 揹包九講(部分)
- 【例9.14】混合揹包
- 動態規劃--01揹包問題動態規劃
- 動態規劃 01揹包問題動態規劃
- 01揹包優先佇列優化佇列優化