01背包问题学习日记 (注:若未学习01背包请首先学习01背包)
今天学习的是01背包的变式——完全背包问题
题目如下
有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。
第 i 种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
完全背包问题与01背包问题极其相似
其唯一的区别在于01背包一件物品只能装一次,而完全背包一件物品可以装多次
下面我们讨论如何推断完全背包问题的状态方程
我们先来看一段01背包的核心代码
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]);
01背包在一维数组下实现我们需要保证第i次循环所用的数据是第i-1次循环的状态,也可以理解为我们在更新第i个物品对一维数组的影响时应在已更新了第i-1个物品的影响的基础上进行,所以01背包在里循环里需要用从大到小的顺序来更新,而在完全背包问题中我们不需要考虑这样的问题,我们只需要考虑维护第i个物品对一维数组的影响即可(不用在意数组是否是从第i-1次循环里继承的)
核心代码如下
for(int i = 1;i <= n;i ++)
for(int j = v[i];j <= m;j ++)
f[j] = max(f[j],f[j - v[i]] + w[i]);
我们可以看到完全背包的代码与01背包的代码的区别仅在于一个是顺向循环一个是逆向循环,对于这一点理解起来可能有一点困难,但其实我在01背包里讨论过这个问题,在这里你可以理解为我们在更新第i件物品的时候一维数组可能已经受了第i个物品的影响,因为是顺序进行的,所以我们是先更新了小容量的背包再更新大容量的背包,导致我们在改变状态时的一维数组可能已经受了第i个物品的影响
最后是AC的代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1010;
int n,m;
int v[maxn],w[maxn];
int f[maxn];
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 = v[i];j <= m;j ++)
f[j] = max(f[j],f[j - v[i]] + w[i]);
cout << f[m];
return 0;
}