背包

背包问题用于解决子集权值和问题,一般会有体积等限制

基本模型

  • \(01\) 背包

这个似乎没啥好说的,注意倒序枚举即可


  • 完全背包

也没啥好说的,改成正序即可


  • 分组背包

注意枚举顺序先组,后倒序体积,最后元素


  • 多重背包

这个直接当成分组背包做肯定是吃不消的
一般常见好写的是二进制拆分,即把那么多物品拆分成 \(log\)

来记录一下单调队列优化的方法:
首先写出来转移方程:

\[f[j]=max_{1\le k\le c}f[j-k*w]+k*v \]

可以发现每个 \(w\) 的同余系的转移是独立的,首先用前缀和转化为加法形式

即改写为 \(f[j]=max(f[d+wk]-vk)+vs\),其中 \(s-c\le k\le s\),典型的滑动窗口了,单调队列来维护

实现见 P1776 宝物筛选


记录一下另一个蓝书上提到的算法,用于 \(O(nm)\) 运行 \(bool\) 类型的多重背包问题
对于每一种物品,开一个数组 \(used\) 表示到达体积 \(j\) 最少用几个当前种物品,如果能从 \(i-1\) 个物品转移为零


  • 退背包

P4141 消失之物

即遵循怎么来的怎么回去
每次将 \(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\) 的体积,剩余的跑暴力


例题

P1941 [NOIP2014 提高组] 飞扬的小鸟

算是藏得比较深的背包题,要学会转化


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]\)


CF1442D Sum

可以发现不选完的序列最多一个
那么枚举每一个序列的前几个,和剩下的背包合并
可以发现这个并不能退背包,那么采用类似于 \(CDQ\) 分治的套路,递归左区间则把右区间放入,否则放左区间


P3188 [HNOI2007]梦幻岛宝珠

大体积背包,数据范围还不能折半搜索
那必然有特殊要求啦,就是 \(a\times 2^b\)

考虑一位一位进行 \(dp\),设 \(f[i][j]\) 表示 \(b=i\) 的物品的 \(a\) 所占用 \(j\) 体积的背包价值,\(g[i][j]\) 表示前 \(i\) 个物品,且相对于第 \(i\) 位体积为 \(j\) 的最大价值

考虑转移,\(f\) 的转移很常规,\(g\) 的转移:

\[g[i][j]=f[i][j-k]+g[i-1][2*k+((m>>(i-1))\&1)] \]

相当于是当前位的体积过渡到下一个的过程,一个顶俩


AT4120 [ARC096D] Sweet Alchemy

考虑每次选择一个子树,只要 \(c'_ i\le d\) 就可以满足次数条件
此时就变成了一个多重背包问题
可以发现体积很大而价值很小,使用一种非常神奇的方法:
对于每个物品选出 \(n\) 个来跑多重背包的 \(dp\) ,其余的直接性价比排序贪心来选,这样就不会带来整除对答案的影响了
其中需要交换体积和价值的下标


P8392 [BalticOI 2022 Day1] Uplifting Excurs

这个也是类似的问题,可以先通过贪心把值域缩小到 \(m^2\)\(dp\) 范围
有负数时,可以先贪心地把负数都选上,然后最后再在做背包的时候去掉

posted @ 2022-08-26 19:49  y_cx  阅读(47)  评论(0编辑  收藏  举报