01 揹包
\(n\) 件物品,每件物品有權值和重量,給出揹包體積 \(V\),從這些物品中挑選若干件(只能選一次)放入揹包,使得揹包內物品的總重量不超過 \(V\),問能可以得到的最大權值。
設 \(dp[i][j]\) 選取前 \(i\) 件物品重量為 \(j\) 能取得的最大的權值,可以得到轉移方程 \(dp[i][j]=max(dp[i-1][j-w[i]]+val[i],dp[i-1][j]);\)
因為 i 只與 i-1 有關所以考慮滾動陣列:
交替滾動
用 now 表示現在的陣列,用 old 表示上一個陣列,交替使用。
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int t,m;
int w[202],val[202];
int dp[3][10010];
int main(){
ios::sync_with_stdio(false);
cin>>t>>m;
for(int i=1;i<=m;i++){
cin>>w[i]>>val[i];
}
int now=0,old=1;
for(int i=1;i<=m;i++){
swap(old,now);
for(int j=0;j<=t;j++){
if(j>=w[i]){
dp[now][j]=max(dp[old][j],dp[old][j-w[i]]+val[i]);
}
else{
dp[now][j]=dp[old][j];
}
}
}
cout<<dp[now][t];
return 0;
}
自我滾動
因為體積只會向小減少,所以從後向前遍歷容量以確保每個數物品只選一次,上一次的陣列儲存的數(i-1) 結果全部就在這個陣列裡,這樣空間壓縮到一維。
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int t,m;
int w[202],val[202];
int dp[10010];
int main(){
ios::sync_with_stdio(false);
cin>>t>>m;
for(int i=1;i<=m;i++){
cin>>w[i]>>val[i];
}
for(int i=1;i<=m;i++){
for(int j=t;j>=w[i];j--){
dp[j]=max(dp[j],dp[j-w[i]]+val[i]);
}
}
cout<<dp[t];
return 0;
}
完全揹包
\(n\) 件物品,每件物品有權值和重量,給出揹包體積 \(V\),從這些物品中挑選若干件(可以多次同選一個)放入揹包,使得揹包內物品的總重量不超過 \(V\),問能可以得到的最大權值。
我們對 01 揹包的程式碼進行更改為從小到大列舉容量,因為在計算某個容量時,可以確保之前計算的狀態已經包含了該物品之前選擇的次數,這樣就能夠正確地累積多個相同物品的價值。
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int t,m;
int w[202],val[202];
int dp[10010];
int main(){
ios::sync_with_stdio(false);
cin>>t>>m;
for(int i=1;i<=m;i++){
cin>>w[i]>>val[i];
}
for(int i=1;i<=m;i++){
for(int j=w[i];j<=t;j++){
dp[j]=max(dp[j],dp[j-w[i]]+val[i]);
}
}
cout<<dp[t];
return 0;
}
多重揹包
\(n\) 件物品,每件物品有權值和重量,給出揹包體積 \(V\),從這些物品中挑選若干件(給出共有幾個)放入揹包,使得揹包內物品的總重量不超過 \(V\),問能可以得到的最大權值。
考慮將每個物品進行二進位制拆分,拆完後就轉化為 01 揹包問題。(因為是二進位制,所以可以保證可以取到任意組合)
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int t,m;
int w[N],val[N];
long long dp[N];
int main(){
ios::sync_with_stdio(false);
cin>>m>>t;
int a,b,c;
int cnt=0;
for(int i=1;i<=m;i++){
cin>>a>>b>>c;
for(int j=1;j<=c;j<<=1){//二進位制
w[++cnt]=j*a;//第cnt個揹包的重量
val[cnt]=j*b;//計算權值
c-=j;//計算剩餘
}
if(c){//剩餘單獨分組
w[++cnt]=c*a;
val[cnt]=c*b;
}
}
for(int i=1;i<=cnt;++i){//01揹包選擇
for(int j=t;j>=w[i];--j){
dp[j]=max(dp[j],dp[j-w[i]]+val[i]);
}
}
cout<<dp[t];
return 0;
}