原题传送门
一道01背包的入门 & 模板题,以下主要提供两种思路
(Solution 1)
看到这道题,首先想到的就是用二维数组来(DP)
状态的表示:(f[i][j])表示前(i)个总重量不超过(j)的最大价值
状态的转移:(f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+c[i]) (w[i]<=j))
注:(w[i])表示第(i)个物品的重量,(c[i])表示第(i)个物品的价值,(j)表示当前的最大重量
最优解:(f[n][m])
注:(n)指物体数量,(m)指最大重量
(Code)
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
inline void read(int &x){
int f=1;
char ch=getchar();
x=0;
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
x*=f;
}
int m,n;
int f[201][201]={0};
int w[31],c[31];
int main(){
read(m);read(n);
for(int i=1;i<=n;i++){
read(w[i]);
read(c[i]);
}
for(int i=1;i<=n;i++){ //当前物体编号
for(int j=m;j>0;j--){ //当前最大重量
if(w[i]<=j) f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+c[i]);
else f[i][j]=f[i-1][j];
}
}
printf("%d",f[n][m]);
return 0;
}
(Solution 2)
显然,二维数组的空间占用非常大。如果背包的最大空间再大一些,就会喜提(MLE)的好成绩
那就有一种新的方法:
状态的表示:(f[j])表示不超过(j)重量的最大价值
状态的转移:(f[j]=max(f[j],f[j-w[i]]+c[i]))
注:(i)依然表示物品编号
最优解:(f[m])
注:(m)表示最大重量
(Code)
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
inline void read(int &x){
int f=1;
char ch=getchar();
x=0;
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
x*=f;
}
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
inline void read(int &x){
int f=1;
char ch=getchar();
x=0;
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
x*=f;
}
int m,n;
int f[201]={0};
int w[31],c[31];
int main(){
read(m);read(n);
for(int i=1;i<=n;i++){
read(w[i]);
read(c[i]);
}
for(int i=1;i<=n;i++){
for(int j=m;j>=w[i];j--){ //j>=w[i]保证一定能装下
f[j]=max(f[j],f[j-w[i]]+c[i]);
}
}
printf("%d",f[m]);
return 0;
}
看到最后,可能会有一个问题:为什么需要倒序循环?
观察状态转移方程:(f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+c[i]) (w[i]<=j))
(f[i][j])的值只与上一行的值(f[i-1][])有关
更新的时候需要用到旧值,倒序防止循环被覆盖。