回退背包

回退背包问题(线段树分治):

\(Content\)

给定\(n\)个物品,编号为\(i\)的物品有质量\(w_i\)和价值\(v_i\)以及一个体积\(V\)。初始时背包没有可选物体。

\(m\)次操作,对于每次操作,给出一个整数\(op\)\(x\)

\(op=1\),向背包的可选范围内添加\(x\)号物品;
\(op=2\),从背包的可选范围内删除\(x\)号物品。

每次操作后求01背包的方案数或最大值。

\(Solution:\)

对于方案数:

求方案数的\(dp\)代码如下:

for(int i=1;i<=n;i++)
    for(int j=V;j>=w[i];j--)
        f[j]+=f[j-w[i]];

考虑以下一个问题,若每次将一个物体放进可选范围后,下一次操作便将它取出,求方案数呢?
只需要把这件物品的贡献再减去即可,注意要正序枚举
易得以下代码:

for(int j=w[i];j<=V;j++)
    f[j]-=f[j-w[i]];

而在背包问题中,物体放入背包的顺序对于答案是没有影响的,所以回退背包关于方案数的解法就等价于上面的解法。

板子题
code

对于最大值:

可以发现假发加法是可逆的,但是取\(max\)是不可逆的,所以关于方案数的解法对于最大值问题自然不适用。

对于最大值问题,可以采用线段树分治来做。

首先离线下来所有询问,记录每个物品的放入时间和取出时间。在时间轴上建立一棵线段树,对于线段树的每个节点都开一个\(dp\)数组,然后将每个物品类放入与其有交的区间中。
然后遍历线段树,每一个节点的\(dp\)数组都可以由它的父亲转移而来(父节点的\(dp\)数组加上新增的物品)。
最后每个叶节点的答案就是每个时间点的答案。

但是这样处理空间复杂度太大了。
而我们其实只需要对每一层开一个\(dp\)数组,每次递归重新赋值就足以记录了。

线段树分治板子
code

回退背包维护最大值板子
code

\(Summary:\)

对于回退背包问题,通解是线段树分治,对于求方案数的题目,可以使用一些小技巧来解决。

posted @ 2025-11-16 22:10  Lmx__qwq  阅读(3)  评论(0)    收藏  举报