背包
背包问题用于解决子集权值和问题,一般会有体积等限制
基本模型
- \(01\) 背包
这个似乎没啥好说的,注意倒序枚举即可
- 完全背包
也没啥好说的,改成正序即可
- 分组背包
注意枚举顺序先组,后倒序体积,最后元素
- 多重背包
这个直接当成分组背包做肯定是吃不消的
一般常见好写的是二进制拆分,即把那么多物品拆分成 \(log\) 个
来记录一下单调队列优化的方法:
首先写出来转移方程:
可以发现每个 \(w\) 的同余系的转移是独立的,首先用前缀和转化为加法形式
即改写为 \(f[j]=max(f[d+wk]-vk)+vs\),其中 \(s-c\le k\le s\),典型的滑动窗口了,单调队列来维护
实现见 P1776 宝物筛选
记录一下另一个蓝书上提到的算法,用于 \(O(nm)\) 运行 \(bool\) 类型的多重背包问题
对于每一种物品,开一个数组 \(used\) 表示到达体积 \(j\) 最少用几个当前种物品,如果能从 \(i-1\) 个物品转移为零
- 退背包
即遵循怎么来的怎么回去
每次将 \(f\) 复制到 \(g\),然后 \(g[j]-=g[j-w[i]]\) 即可,注意要正序枚举
注意 \(max\) 以及可行性时均不能使用退背包
- \(bitset\) 优化可行性背包
注意到转移方程为 \(f[j]|=f[j-a[i]]\),那么用 \(bitset\) 优化成 \(f|=f<<a[i]\)
不用担心 \(bitset\) 的 \(RE\) 问题,会自动省略超出的部分
- 负体积背包
对于负体积的物品,需要改变循环顺序,因为其加减号发生了改变,那么循环顺序相当于还是保证了转移顺序
- 大容量背包
对于容量很大,而物品体积很小的背包,可以先贪心用性价比最高的选择到 \(\prod w\) 的体积,剩余的跑暴力
例题
算是藏得比较深的背包题,要学会转化
CF687C The Values You Can Make
背包问题的变形
设 \(f[j][k]\) 表示和为 \(j\) 且子集和为 \(k\) 可不可行
那么有两种转移,即每个放入 \(j\) 的数放不放入 \(k\)
那么 \(f[j][k]|=f[j-a[i]][k-a[i]]\)
以及 \(f[j][k]|=f[j-a[i]][k]\)
可以发现不选完的序列最多一个
那么枚举每一个序列的前几个,和剩下的背包合并
可以发现这个并不能退背包,那么采用类似于 \(CDQ\) 分治的套路,递归左区间则把右区间放入,否则放左区间
大体积背包,数据范围还不能折半搜索
那必然有特殊要求啦,就是 \(a\times 2^b\)
考虑一位一位进行 \(dp\),设 \(f[i][j]\) 表示 \(b=i\) 的物品的 \(a\) 所占用 \(j\) 体积的背包价值,\(g[i][j]\) 表示前 \(i\) 个物品,且相对于第 \(i\) 位体积为 \(j\) 的最大价值
考虑转移,\(f\) 的转移很常规,\(g\) 的转移:
相当于是当前位的体积过渡到下一个的过程,一个顶俩
AT4120 [ARC096D] Sweet Alchemy
考虑每次选择一个子树,只要 \(c'_ i\le d\) 就可以满足次数条件
此时就变成了一个多重背包问题
可以发现体积很大而价值很小,使用一种非常神奇的方法:
对于每个物品选出 \(n\) 个来跑多重背包的 \(dp\) ,其余的直接性价比排序贪心来选,这样就不会带来整除对答案的影响了
其中需要交换体积和价值的下标
P8392 [BalticOI 2022 Day1] Uplifting Excurs
这个也是类似的问题,可以先通过贪心把值域缩小到 \(m^2\) 的 \(dp\) 范围
有负数时,可以先贪心地把负数都选上,然后最后再在做背包的时候去掉