题目来源:acwin
方法一:(二维数组)
状态转移方程dp[ i ] [ j ]:
(设第i件物品的体积和价值分别为w[ i ]和val[ i ])
如果第i件物品的体积大于容量j,则物品i不能装入背包,此时dp[ i ] [ j ]=dp[ i-1 ] [ j ]。如果第i件物品的体积不大于容量j,则分两种情况,一是把第i件物品装入背包,此时dp[ i ] [ j ]=dp[ i-1 ] [ j-w[i] ]+val[ i ];二是不把第i件物品装入背包,此时dp[i][j]=dp[i-1][j]。取二者中大的为最终dp[i][j],即dp[i] [j]=max( dp[ i-1 ][ j-w[i] ]+val[ i ], dp[ i-1 ][ j ] )
#include<iostream>
#include<cmath>
using namespace std;
int w[1010],v[1010];
int dp[1010][1010];
int main(){
int N,Vol;
cin>>N>>Vol;
for(int i=1;i<=N;i++){
cin>>v[i]>>w[i];
}
for(int i=1;i<=N;i++){
for(int j=1;j<=Vol;j++){
if(j<v[i]){
dp[i][j]=dp[i-1][j];
}
else{
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);
}
}
}
cout<<dp[N][Vol]<<endl;
return 0;
}
另外,v[i]和w[i]可以边输入边处理,这样就可以节省处理数据的时间。
#include<iostream>
#include<cmath>
using namespace std;
int dp[1010][1010];
int main(){
int N,Vol;
int v,w;
cin>>N>>Vol;
for(int i=1;i<=N;i++){
cin>>v>>w;
for(int j=1;j<=Vol;j++){
if(j<v){
dp[i][j]=dp[i-1][j];
}
else{
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v]+w);
}
}
}
cout<<dp[N][Vol]<<endl;
return 0;
}
方法二:
由于dp[ i ]只与dp[ i-1 ]有关,所以如果想要节省空间,定义dp[2] [1010]就行了。即来回覆盖。
int solve(int N,int Vol){
int _new=0,old=1;
for(int i=1;i<=N;i++){
swap(_new,old);
for(int j=0;j<=Vol;j++){
if(v[i]>j)dp[_new][j]=dp[old][j];
else{
dp[_new][j]=max(dp[old][j],dp[old][j-v[i]]+w[i]);
}
}
}
return dp[_new][Vol];
}
方法三:(一维数组)
按二维数组来讲,我们只需要输出dp[N][Vol]就行了,但我们却求出了从1到N的所有情况。因此我们用一个一维数组来回覆盖也能求出最终的结果。对于N来说,只涉及到dp[i]=dp[i-1],因此我们从小到大进行覆盖不会影响最终的结果。但对于体积Vol来说,我们可能会用第 i-1 轮的较小体积 j-v[i]来更新成第 i 轮的较大体积 j,如果体积Vol也是从小到大,那么我们将先更新体积小的,也就是说我们将用第 i 轮的较小体积 j-v[i] 更新得到第 i 轮的较大体积 j,这显然是错误的,因为我们应该用第 i-1 轮的较小体积 j-v[i] 来更新第 i 轮的较大体积 j。但如果对于体积,我们从大到小进行循环则可以避免这个错误,因为当从大到小进行循环时,较小体积 j-v[i] 还未被更新,也就是说用的是第 i-1 层的较小体积 j-v[i] ,而不是第 i 层的 j-v[i]。
#include<iostream>
#include<cmath>
using namespace std;
int dp[1010];
int main(){
int N,Vol;
int v,w;
cin>>N>>Vol;
for(int i=1;i<=N;i++){
cin>>v>>w;
for(int j=Vol;j>=1;j--){
if(j<v){
dp[j]=dp[j];
}
else{
dp[j]=max(dp[j],dp[j-v]+w);
}
}
}
cout<<dp[Vol]<<endl;
return 0;
}
另外,上述代码中的if语句 dp[j]=dp[j] 可省略,且只有在 j>=v 时才会执行语句 dp[j]=max(dp[j],dp[j-v]+w); 因此代码可以进一步简化为
#include<iostream>
#include<cmath>
using namespace std;
int dp[1010];
int main(){
int N,Vol;
int v,w;
cin>>N>>Vol;
for(int i=1;i<=N;i++){
cin>>v>>w;
for(int j=Vol;j>=v;j--){
dp[j]=max(dp[j],dp[j-v]+w);
}
}
cout<<dp[Vol]<<endl;
return 0;
}