题目大意很简单,一共有N种骨头,每种骨头有不同的体积和价值,书包的体积为V,在装入骨头不超过书包体积的前提下,使得放入书包骨头的总价值最大。
首先输入T,一共有T组数据,接着输入N和V,分别代表骨头的种类数与书包的容积、接着输入value[1]-value[N],每个骨头的价值,最后输入volume[1]-volume[N],每个骨头的体积。
典型的01背包问题。设置数组f,f[i]表示书包容积为i的时候,装入书包骨头的最大价值。
设f[i](j)表示书包容积为i,从前j个骨头中选择,装入书包骨头的最大价值。目标即求f[V](N)。那么问题的核心就是求解f[i](j),在前j个骨头中选择,书包容量为i,装入书包的骨头最大价值分为三种情况:1、把第j个骨头不装入书包,在这种情况下,装入书包骨头的最大价值等于书包容量为i,从前j-1个骨头中选择,装入书包骨头的最大价值。2、把第j个骨头装入书包。此时求最大价值,把第j个骨头装入后,获得了value[j]的价值,书包容量变成i-volume[j],再求f[ i-volume[j] ](j-1)即可。3、书包的容积小于第j个骨头的体积,此时第j个骨头没用,最大价值等于从容积为i,从前j个骨头中选择的最大价值f[i](j-1)。
所以f[i](j)=max (f[i](j-1) , f [i- volume[j] ] (j-1) + value[j] )
所以求解的代码可以写成:
for(j=1;j<=N;j++){
//求解f[V](j)
for(i=V;i>=0;i--){
f[i]=max(f[i] , f[i-volume[j]]+value[j]);
}
}
保证等号左边的是(j)而等号右边的是(j-1)的方法则是让内层循环逆序。如果理解逆序有些费劲的话,可以直接带几个特殊值进去,先手动运行一次内层循环正序的代码,再手动运行内层循环逆序的代码,就会发现内层循环如果正序的话根本就没有办法得出正确结果。反正网上的一些教程对于内层循环为何不能正序解释得也不是那么的清楚,我学01背包问题的时候就是通过特殊值手动运行代码理解的。最后循环退出的时候输出f[N]即可。
掌握了上面的核心代码的话,整个题目解出来就很容易了。
#include <iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int value[1002];//价值
int volume[1002];//体积
int f[1002];//f[i]:书包体积为i,最大价值
int main(){
int N,V;//骨头数量、书包体积
int T;
cin>>T;
for(int i=1;i<=T;i++){
memset(f,0,sizeof(f));
cin>>N>>V;
for(int j=1;j<=N;j++) cin>>value[j];
for(int j=1;j<=N;j++) cin>>volume[j];
for(int j=1;j<=N;j++){
for(int k=V;k>=0;k--){
//书包体积为k,从前j个骨头中选择,最大价值
if(k<volume[j]) break;
else{
f[k]=max(f[k],f[k-volume[j]]+value[j]);
}
}
}
cout<<f[V]<<endl;
}
}
注意内层对于书包容积循环必须到0,因为书包的容积可能为0,输入骨头的体积也可能为0,骨头的价值不为0。此时输出的就是f[0]。我就是一开始没考虑到这个问题而一直WA。