原始状态转移式为:
f[i][j]=max(f[i][j],f[i-1][j-w[i]+v[i])
i表示是否放入第i个物品,j表示此时背包已占有容量。
max()第一个参数表示背包已满,放不下第i个物品,第二个参数表示背包正好能容纳第i个物品。
由于不放入第i个物品时,背包总价值等于上一个状态,即放与不放第i-1个物品的最大值。由此可得:
if(不放第i个物品)
f[i][j]=f[i-1][j];
所以上面第状态转移式可写为:
f[i][j]=max(f[i-1][j],f[i-1][j-w[i]+v[i])
程序如下:
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 1001;
int w[MAXN]; // 重量
int v[MAXN]; // 价值
int f[MAXN][MAXN];
int main()
{
int n, m, s;
cin >> n >> m;
for(int i = 1; i <= n; ++i)
cin >> w[i] >> v[i];
for(int i = 1; i <= n; ++i)
for(int j = 0; j<=m; j++)
{
if(m<w[i])
f[i][j]=f[i-1][j];
else
f[i][j] = max(f[i-1][j], f[i-1][j-w[i]]+v[i]);
}
cout<<f[n][m]<<endl;
return 0;
}
下面进行降维操作。已知状态式与 f [ i ] 有关,左右两边同时消去 f [ i ]和 f [ i -1 ],得:
f[j] = max(f[j], f[j-w[i]]+v[i]);
此时我们发现问题:式子中所有的 f 都是在状态 i 下的,我们原本是想通过 i - 1 的状态判断加不加第 i 个物品,现在却变成了由 f [ j ] [ j - w [ i ] ] 推 f [ j ] 。解决方法是反向 for 循环,即保证推 f [ j ] 时, f [ j - w [ i ] ] 保存的是状态 f [ j - 1 ] [ j - w [ i ] ]的值。
#include <iostream
#include <algorithm>
using namespace std;
const int MAXN = 1001;
int w[MAXN]; // 重量
int v[MAXN]; // 价值
int f[MAXN];
int main()
{
int n, m, s;
cin >> n >> m;
for(int i = 1; i <= n; ++i)
cin >> w[i] >> v[i];
for(int i = 1; i <= n; ++i)
for(int j = m; j>=w[i]; j--)
f[j] = max(f[j], f[j-w[i]]+v[i]);
cout<<f[m]<<endl;
return 0;
}