有 N 件物品和一个容量是 V 的背包,每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi 。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大
定义状态 f[i][j] 表示:前 i 件物品 当体积不超过 j 时的所有选法的集合
状态方程 f[i][j] 的状态转移关键在于 第 i 件物品选或不选;
f[i][j] = f[i - 1][j];
f[i][j] = max(f[i - 1][j], f[i - 1][j - v] + w);
int f[N][N];
int v[N], w[N];
int main()
{
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 = 1; j <= m; j++)
{
f[i][j] = f[i - 1][j];// 第 i 个物品一定可以不选,但不一定可以选
if (j >= v[i]) f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
}
}
cout << f[n][m] << endl;
}
01背包优化的实质是 对代码的等价替换;
(这里我们先将优化 前后 的主要代码放出)
//优化前
for (int i = 1; i <= n; i++)
{
for (int j = 1; 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]);
}
}
//优化后
for(int j = m; j >= v; j--)
f[j] = max(f[j], f[j - v] + w);
我们的思路是:
1.不考虑影响 直接去掉 i
for (int i = 1; i <= n; i++)
{
for (int j = 1; 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]);
} //将外层循环i去掉,并将状态方程f[i][j]转移为f[j]
//若不考虑影响,直接变化
//变化前 :f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
//变化后: f[j] = max(f[j], f[j - v] + w);
}
2.我们试着分析一下变化后的该代码段的意义以及差异:
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (j >= v[i]) f[j] = max( f[j], f[j - v] + w );
} // f[i][j] f[i-1][j] f[i][j - v]
//优化前 f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
} //第一个f[j] 是在第i层计算出 所以实质为f[i][j],
//第二个f[j] 是在第i - 1层计算出的 所以实质为f[i - 1][j]
//而f[j - v], 由于第二层循环是从小到大 易知j - v < j;
//因此f[j - v] 早于f[j]计算出 所以实质为f[i][j - v]
//但优化前f[i - 1][j - v[i]] 可知f[j - v] 晚于f[j]计算出
3.处理:
循环倒序进行, 从而使f[j - v] 早于f[j]计算出
for(int j = m; j >= v; j--)
f[j] = max(f[j], f[j - v] + w);