背包 学习笔记
前言:背包学了无数遍,这次总算能记住点东西了QAQ
----------------
背包是线性DP中一类重要而特殊的模型。
0/1背包
给定$n$个物品,其中第$i$个物品的体积为$w_{i}$,价值为$c_{i}$。现在给你一个体积为$m$的背包,问怎样选择使得物品总价值最大?
我们先考虑朴素算法。
设$f[i][j]$为已经考虑了前$i$个物品,选出总体积为$j$的物品放入背包所取得的最大价值。
如果不选第$i$件物品,则$f[i][j]=f[i-1][j]$。
如果选第$i$件物品,则$f[i][j]=f[i-1][j-w_{i}]+c_{i} (j\geq w_{i})$。两者中取最大值。
考虑优化:因为在考虑第$i$件物品时只取决于考虑第$i-1$件物品时的情况,所以上面$f$数组的第一维可以省去。
即$f[j]=max(f[j],f[j-w[i]]+c[i])$。
请注意:$j$是倒序循环的。假设正序循环:比如用$j-2w_{i}$的情况来更新$j-w{i}$,此时$j-w{i}$已经过度到第$i$个阶段,再循环到$j-w{i}$时,就会发生“第$i$个阶段更新第$i$个阶段”的情况,违背了线性DP的原则。而倒序循环保证了每个物品只会被考虑一次,符合线性DP的原则。
-------------------------------
完全背包
和0/1背包差不多,只是增添了一个条件:每个物品可以被挑选无数次。
和0/1背包唯一不同的地方:$j$是正序循环的。因为每个物品都可以被选无数次。
$f[j]=max(f[j],f[j-w_{i}]+c_{i}$。
-------------------------------------
多重背包
和完全背包的条件不同:每个物品可以被选$t_{i}$次。
最朴素的方法当然是从$0$到$t_{i}$循环,每种情况都试一遍。但复杂度较高。
我们可以用二进制拆分法。众所周知,$2^0,2^1,2^2,...,2^k-1$可以表示$0$到$2^k -1$的所有整数。所以我们可以用这种方法把物品二进制拆分。设$r_{i}=c_{i}-2^0-2^1-...-2^p$,则可以拆分为$p+2$个物品,体积分别为$2^0 *w_{i},2^1 *w_{i},...,2^p *w_{i},r_{i}*w_{i}$。此方法将物品拆分为$logc_{i}$个,效率较高。
------------------
分组背包
条件变化:有$i$组物品,每组物品有$k$个,每组最多选一个物品,求最大价值。
直接让$k$循环选取即可,只要选完了就立即从第$i$个阶段过渡到第$i+1$个状态。
for (int i=1;i<=n;i++) for (int j=m;j>=0;j--) for (int k=1;k<=c[i];k++) if (j>=v[i][k]) f[j]=max(f[j],f[i-v[i][k])+w[i][k]
注意$k$循环要在$j$循环之内。