题目
想法1
采用三层循环,尝试第i种装k个的情况,只要第i中装k个的体积<j就比较采用前i个装j容量和采用前i-1个装j-k*v[i]容量价值+k*w[i]价值之和哪个比较大
#include<iostream>
using namespace std;
const int MAX=1005;
int w[MAX];
int v[MAX];
int f[MAX][MAX];
int max(int a,int b){
if(a>b) return a;
else return b;
}
int main(){
int N,V,j;
cin>>N>>V;
for(int i=1;i<=N;i++){
cin>>v[i]>>w[i];
}
for(int i=1;i<=N;i++){
for(j=1;j<=V;j++){
for(int k=0;k*v[i]<=j;k++)
f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
}
}
cout<<f[N][V]<<endl;
}
之前纠结:f[i][j]=max(f[i][j],…)还是=max(f[i-1][j]…),个人认为是都可以的,由于初始条件下f[i][j]为0,所以在第一次max比较的时候肯定会被修改为一个值。而f[i-1][j]对应的是k=0的情况,也就是在k=0的时候第一次被修改的情况,如果初始设为f[i-1][j]的话,k可以从1开始。
想法2(改进)
可以看出在想法1中采用三层循环。根据分析可知,
f [ i ][ j ] = max(f[ i-1 ][ j ],f[ i-1 ][ j-v [ i ] ]+w[ i ],f[ i-1 ][ j-2*v[ i ] ]+2*w[ i ],…)
f [ i ][ j-v[ i ] ]=max(f[ i-1 ][ j-v[ i ] ],f[ i-1 ][ j-2*v[ i ] ]+w[ i ],…)
所以,f[ i ][ j ]=max(f[ i -1 ][ j ],f[ i ][ j-v[ i ] ]+w[ i ])
f [ i ][ j-v[ i ] ]+w[ i ]这句话的意思是在当前的 i 作为一维的情况下,不断使用前面已经更新过的 f [ i ][ 前面体积 ]来更新 f [ i ][ 后面的体积 j ] 。当 j >= v [ i ] 的时候,就会尝试加入一个 i 物品,并与前面 i - 1 个物品的方案作比较,如果加入一个 i 物品的方案的价值大于原先同体积时的方案,那么就会更新这个体积 j 时的方案价值,所以如果后面的 j - v [ i ] 得到的体积所对应的最大价值是恰好先前已经更新过的体积 j ,那么这时候得到的方案就是在先前更新的方案中继续加入一个 i 物品(这样就实现了 i 物品的叠加操作),如果新的方案优于旧的方案,那么就更新 f 数组。如此循环.
关键点来了!01背包的递推式为:
f[ i ][ j ]=max(f[ i-1 ][ j ],f[ i-1 ][ j-v[ i ]+w [ i ])
所以可以参照01背包的化简方法,将其化简为一维数组:
f[j]=max(f[j],f[j-v[i]]+w[i])《与01背包表达式相同》:此时的j一定要从前向后更新!!
#include<iostream>
using namespace std;
const int MAX=1005;
int w[MAX];
int v[MAX];
int f[MAX];
int max(int a,int b){
if(a>b) return a;
else return b;
}
int main(){
int N,V,j;
cin>>N>>V;
for(int i=1;i<=N;i++){
cin>>v[i]>>w[i];
}
for(int i=1;i<=N;i++){
for(j=v[i];j<=V;j++){
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<f[V]<<endl;
}
小tips:此时在二层循环中省去了一个if条件:因为将j从v【i】开始,所以不用判断j啦~