01 背包
\(01\) 的意图很明显,就是每个物品有 \(01\),即 选 和 不选 两种方式。
暴力
考虑设定一个状态 \(dp[i][j]\) 表示在前 \(i\) 个当中,花费为 \(j\) 所能获得的最大值。
转移可以:
优化
不难发现当前的 \(dp_{i,j}\) 只和 \(i-1\) 的部分有关系,于是滚动数组即可优化掉一维。
优化后的转移方程如下:
由于修改 \(dp_j\) 的时候需要 \(dp_{j-cost_i}\) 的值,所以需要先修改 \(dp_j\) 中 \(j\) 较大的值。
时间复杂度 \(O(nT)\) 其中 \(T\) 是物品总数。
空间复杂度 \(O(m)\),\(m\) 是背包最大容量。
完全背包
完全 的意义在哪里呢?在于每一个物品可以被选取任意次,而并非 \(01\) 背包的 \(0/1\)。
暴力
仍然是 \(dp_{i,j}\) 表示在前 \(i\) 个当中花费为 \(j\) 时所能得到的最大花费。
首先,考虑一个显然的做法,枚举每一个物品的出现次数:
优化
考虑如何去掉枚举 \(\max\) 的一维。
不难发现,只需要考虑 \(dp_{i,j}\) 和 \(dp_{i-1,j-cost_i}+value_i\) 两部分即可。可为什么呢?
尝试使用数学归纳法来证明。
根据 \(01\) 背包的经验,可以很轻松的压掉一维。
多重背包
如何理解 多重 的意义呢?
意义比较容易理解,就是每一个物品只有有限个 \(num_i\)。
暴力
首先,还是有一个很暴力的解法:
考虑将所有的物品看成 \(num_i\) 个物品,然后直接 \(01\) 背包。
时间复杂度是 \(O(m\displaystyle \sum_{i=1}^n num_i)\)。
二进制优化
熟知,我们可以使用 \(k\) 位二进制来表示 \(1\) 到 \(2^k\) 的所有数。
首先设 \(2^{\alpha_i+1}\le num_i < 2^{\alpha_i+2}\)
将 \(num_i\) 拆分成 \(\displaystyle\sum_{j=1}^{\alpha_i}2^i+(num_i-2^{\alpha_i+1}+1)\),这样就可以表示出 \(1\) 到 \(num_i\) 之间的所有数。
之后再次用 \(01\) 背包就可以了。
时间复杂度为 \(O(m\displaystyle \sum_{i=1}^n \log_2num_i)\)
混合背包
何为混合?
就是将前面三种背包加起来。
非常简单。
二维费用背包
如果限制有两位呢,怎么求解?
设置状态 \(dp_{i,j,k}\) 表示在前 \(i\) 个物品中,限制 \(1\) 的花费为 \(j\),限制 \(2\) 的花费为 \(k\) 时所能得到的最大价值。
由于此时是三维的,所以需要使用 \(01\) 背包的方法再次优化掉第一维。
然后,枚举 \(i,j,k\) 即可。
时间复杂度 \(O(nT_{限制1}T_{限制2})\)。
分组背包
考虑先枚举组数,然后再每一组中做 \(01\) 背包。
时间复杂度 \(O(nm)\) 其中 \(n\) 是 \(k\) 组的物品的总种类。
有依赖的背包
有两种方法: