01背包
有N个物品和一个容量为V的背包, 物品有两个属性: 价值和体积
第i个物品的价值为wi,体积为vi
体积为整数
状态定义为
f(i,j)=只考虑前i件物品,背包容量为j时能获取的最大价值
状态转移方程可以这样考虑:考察状态f(i,j), 对于i个物品可以选择拿走或者不拿走, 如果不拿走则
f(i,j)=f(i−1,j)
拿走则需要腾出vi的空间,即上一步只有j−vi的空间可以用
f(i,j)=f(i−1,j−vi)+wi
由此得到状态转移方程
f(i,j)=max{f(i−1,j),f(i−1,j−vi)+wi}
鉴于f(i,j)只依赖于i−1的状态, 可以使用滚动数组减小空间开销
多重背包
有N种物品和一个容量为V的背包, 物品有三个属性: 数量,价值和体积
第i种物品的价值为wi,体积为vi,数量为si
状态依然定义为
f(i,j)=只考虑前i件物品,背包容量为j时能获取的最大价值
考察状态f(i,j)时, 需要遍历拿0个,拿1个, ... ,拿si个情况, 那么状态转移方程就是
f(i,j)=max{f(i−1,j−k⋅vi)+k⋅wi},k=0,1,⋯,si
鉴于f(i,j)只依赖于i−1的状态, 可以使用滚动数组减小空间开销
也可以将其转化为01背包问题, 即将第i种物品变为si件物品. 不过这样朴素地转化, 时间复杂度是相同的, 这不好. 可以优化的点在于考虑了多种相同的情况
0,0,1,1与0,1,0,1拿的数目是相同的
一种聪明的方式是利用二进制拆分物品, 像这样
1,2,4,⋯2n
任一拿物品的方案都可以用这些物品组的拿与不拿状况唯一表示, 这样就去除了重复的情况, 时间复杂度将为了
V∑logsi
完全背包
有N种物品和一个容量为V的背包, 物品有两个属性: 价值和体积
第i种物品的价值为wi,体积为vi
每种物品的数目无限多
一种朴素的做法是如多重背包一般
f(i,j)=max{f(i−1,j−k⋅vi)+k⋅wi},k=0,1,⋯
不过这里的k选择并没有上限, 唯一约束是不能超过背包容量. 这样做显然复杂度很大, 可以考虑与多重背包相同的二进制优化方案
1,2,4,⋯,2kk=logV/vi
这样就能将复杂度大大降低