题目描述
01背包问题(每个物品只能用一次)
选法共有2^n次
优化方法:DP(在不改变原来代码的基础上进行空间优化,等价变形)
选法:
(1)所有不选第i个物品的方案,各种选法的总价值为f[i-1][j]
(2)所有选择第i个物品的方案,分为其他物品随便选加上物品i的方案,随便选的方案价值为f[i-1][j-v[i]],就规定了j一定大于等于v[i]物品i的价值为w[i],
所以总价值最大要在以上两种方案中选最大的,即max(f[i-1][j],f[i-1][j-vi)
算法1
二维数组f[i][j]表示前i个物品,总体积不超过j,表示的是选法的集合,代表集合当值每一种方案的 总价值
f[N][V]就表示N个物品中,总体积不超过V的总价值最大的方案的集合,因为该集合每个数的值就代表价值,所以在既定的物品数量中,总体积最大的方案的总价值就最大。
所以f[V][N]就是答案
C++ 代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
int f[N][N];
int v[N],w[N];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++) //体积由0开始
{
//递归求选法
f[i][j]=f[i-1][j]; //选到第i个物品前,即不选第i个物品
if(j>=v[i])
f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
}
}
int res=0;
for(int i=0;i<=m;i++) res=max(res,f[n][i]);//循环求到最大的
//但实际上f[n][m]就是最大的
//cout<<f[n][m]<<endl;
cout<<res<<endl;
return 0;
}
算法2
一维数组f[j] 滚动数组
C++ 代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
int f[N];
int v[N],w[N];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
{
for(int j=m;j>=v[i];j--) //从大到小,确保j-v[i]没有重复计算过
{
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<f[m]<<endl;
return 0;
}