完全揹包問題詳解
阿新 • • 發佈:2021-10-23
本文詳細整理了常見的幾類揹包問題的多種解法。內容涉及01揹包問題,完全揹包問題,多重揹包問題,分組揹包問題等。
引言
揹包問題是動態規劃(DP)的一類問題。
揹包問題的核心其實就是組合問題,在一個揹包中有若干物品,在某種限制條件下,選出最好的組合。
完全揹包問題
特點:每件物品有無限個。
有 N 種物品和一個容量是 V 的揹包,每種物品都有無限件可用。 第 i 種物品的體積是 vi,價值是 wi。 求解將哪些物品裝入揹包,可使這些物品的總體積不超過揹包容量,且總價值最大。 輸出最大價值。 輸入格式 第一行兩個整數,N,V,用空格隔開,分別表示物品種數和揹包容積。 接下來有 N 行,每行兩個整數 vi,wi,用空格隔開,分別表示第 i 種物品的體積和價值。 輸出格式 輸出一個整數,表示最大價值。 資料範圍 0<N,V≤1000 0<vi,wi≤1000 輸入樣例 4 5 1 2 2 4 3 4 4 5 輸出樣例: 10
y氏DP分析法:參考自AcWing閆學燦。
三重迴圈(樸素)做法:資料加強後TLE,重在理解寫法。
// y總題解 // 注意:TLE #include <iostream> #include <algorithm> using namespace std; const int N = 1010; int n,m; int dp[N][N]; int v[N],w[N]; int main(){ cin >> n >> m; for (int i = 1;i <= n;i++) cin >> v[i] >> w[i]; // i從1開始列舉,j從0開始列舉 for (int i = 1;i <= n;i++) for (int j = 0;j <= m;j++) for (int k = 0;k*v[i]<=j;k++) dp[i][j] = max(dp[i][j],dp[i-1][j-k*v[i]] + k*w[i]); cout << dp[n][m] << endl; return 0; }
優化:
時間複雜度:O(n*m)。
減少一重迴圈。
對比01揹包問題的狀態轉移方程是:f[i][j] = max(f[i-1][j],f[i-1][j-v[i]]+w[i])
我們很容易發現,01揹包和完全揹包的區別就在於第二項的第一維,前者是i-1
,而後者是i
。
#include <iostream> #include <algorithm> using namespace std; const int N = 1010; int n,m; int dp[N][N]; int v[N],w[N]; int main(){ cin >> n >> m; for (int i = 1;i <= n;i++) cin >> v[i] >> w[i]; for (int i = 1;i <= n;i++) for (int j = 0;j <= m;j++){ dp[i][j] = dp[i-1][j];// 特判第一種情況 if (j >= v[i]) dp[i][j] = max(dp[i][j],dp[i][j-v[i]]+w[i]); } cout << dp[n][m] << endl; return 0; }
因為和01揹包程式碼很相像,我們很容易想到進一步優化。
這裡先介紹降低第一維度的題解,在01揹包中沒有提到過。
就是將第一個維度直接&1,那麼資料就會儲存在dp[0][x]
和dp[1][x]
中。只要用到dp[2][N]
這麼大的陣列就足夠了。(這就是一個兩層的滾動陣列)
我們還可以再優化,邊讀入邊處理。
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n,m;
int dp[2][N];
int v,w;
int main(){
cin >> n >> m;
for (int i = 1;i <= n;i++){
cin >> v >> w;
for (int j = 0;j <= m;j++){
dp[i&1][j] = dp[(i-1)&1][j];
if (j >= v) dp[i&1][j] = max(dp[i&1][j],dp[i&1][j-v] + w);
}
}
cout << dp[n&1][m] << endl;
return 0;
}
接下來是類似01揹包的更優化的滾動陣列。
利用滾動陣列優化成一維:
由於完全揹包用到的dp[i][j-v[i]]
是第i
(即本次)次的結果,不像01揹包一樣用到的是上一次的結果,所以可以直接正向列舉。
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n,m;
int dp[N];
int v[N],w[N];
int main(){
cin >> n >> m;
for (int i = 1;i <= n;i++) cin >> v[i] >> w[i];
for (int i = 1;i <= n;i++)
for (int j = v[i];j <= m;j++){
dp[j] = max(dp[j],dp[j-v[i]]+w[i]);
}
cout << dp[m] << endl;
return 0;
}