我们可以用 v[ i ] ,w[ i ] 表示 第 i 个物品的 体积 和 价值 。
f[ i ] [ j ] 含义是 只考虑前 i 个物品,总体积不超过 j ,最大价值为 f[ i ] [ j ] 。
对于每一个i 都有 选 和 不选 两种情况:
如果不含 i ,那就是 从前 i - 1 个 选重量不超过 j 的物品,即 f( i , j ) = f( i-1 , j ) ;
如果包含 i ,那就是 从前 i - 1 个 选重量不超过 j - vi 的物品 ,即 f( i , j ) = f( i-1, j - vi) +wi;
(含义是 第 i 个物品选了, 那就代表要在前 i - 1 个物品中 选不超过 j - vi 重量的体积,并且总价值要加上wi)
那么 f( i , j ) = max ( f( i-1 , j ) , f( i-1, j - vi) +wi ) ;
我们也可以用表格去模拟一下:
不难看出,打印出来的表和我们模拟的表一样。
#include<iostream> 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++) { f[i][j]=f[i-1][j]; if(j>=v[i]) f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]); } cout<<f[n][m]<<endl; return 0; }
前面其实都是小儿科,最难的地方今天他来了! 是我们可以对这个程序继续优化,可以优化成一维数组,什么意思?我们不难发现我们每一行其实都只与上一行有关系,例如第 n 行 只与 n - 1 行有关系,与 n - 2 没关系,那么 我们可以不听更新迭代一行就行了,就不需要记录 i 了。
一维数组 f [ j ] 的含义是 背包容量是 j 时,能装的最大价值。
第 3 行可以通过 第 2 行来更新,但是有个小detail! 我们必须从后往前去更新迭代。
我们每次计算dp [ j ] (即 dp [ i ] [ j ]) 的时候都会需要 dp [ j - w [i] ] (即 dp [ i - 1 ] [ j - w [i] ])的值。因为 j - w [ i ] 比 j 小,所以如果我们正序计算,那么dp [ j - w [ i ] ] 就已经更新了 (即dp [ i ] [ j - w[ i ] ]),与状态转移方程不符。
#include<iostream>
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--)
f[j]=max(f[j],f[j-v[i]]+w[i]);
cout<<f[m]<<endl;
return 0;
}