题意概要:有背包dp - 图1个物品和一个容量为背包dp - 图2的背包,每个物品有重量背包dp - 图3和价值背包dp - 图4两种属性,要求选若干物品放入背包使背包中物品的总价值最大且背包中物品的总重量不超过背包的容量。

0-1背包

每个物品对应只有取和不取两种状态,对应0和1,并且每种物品只能取一次
假设我们要求的价值函数为背包dp - 图5,含义为背包容量为背包dp - 图6时,放入第k个物品时的总价值,显然我们最终希望得到背包dp - 图7的最大值。
考虑状态的转移。
背包dp - 图8
当第k个要放入的物品的重量超过容量时,那么就不放入,保持不变;如果可以放入时,考虑剩余容量为背包dp - 图9时的价值加上放入物品的价值即可。


简单的写法是外层背包dp - 图10循环,内层背包dp - 图11循环。
由于转移的过程背包dp - 图12只受背包dp - 图13影响,因此可以将二维降成一维。
背包dp - 图14

  1. for(int i = 1; i <= n; i++){
  2. for(int j = C; j >= w[i]; j--){ //注意这行
  3. C[j] = max(C[j], C[j - w[i]] + p[i]);
  4. }
  5. }

第2行不能被写成正向循环,为什么?因为0-1背包要求每个物品只能被取一次,如果写成正向循环,背包dp - 图15的值可以被多次改变,意味着物品背包dp - 图16可以被多次放进背包,这是不符合0-1背包要求的。反向循环就可以解决这个问题,每次更新均保证不会导致已更新的值再更新。
对于这个写法,空间的需求为背包dp - 图17

完全背包

我第一次接触到的应该是完全背包,但是题目上也没有指明,所以和0-1背包搞混了。

完全背包和0-1背包的区别只有物品是否可以重复被选取。
这样我们的循环就应该写成正向循环:

  1. for(int i = 1; i <= n; i++){
  2. for(int j = 0; j <= C - w[i]; j--){
  3. C[j + w[i]] = max(C[j + w[i]], C[j] + p[i]);
  4. }
  5. }