背包 学习笔记

前言:背包学了无数遍,这次总算能记住点东西了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$循环之内。

posted @ 2020-04-13 21:59  我亦如此向往  阅读(139)  评论(0编辑  收藏  举报