若干背包模型 powered by ddxrS
同余最短路与完全背包
经典问题是 luogu P9140,类似题目还有 CF 2115E
给定若干物品 \((v_i,w_i)\),其中 \(w\) 是价值 \(v\) 是占用体积,做完全背包。
不妨默认已经按性价比排序。
其核心结论有如下几个:
-
至多有 \(v^2_{\max}\) 的背包用于放置非第一个物品。
证明:将完全背包看做同余最短路,将每个 \(i\) 连向 \((i+v_j)\bmod v_1\) ,边权为 \(w_i-\lfloor\frac{i+v_j}{v_1}\rfloor w_1\)
因为是选取的性价比最高的在外面,因此不存在负环,那么到任意点的所经过边数不超过 \(v_1\),而每条边最多选取 \(v_{\max}\),因此转移到每个点实际上最多选取了 \(v_1v_{\max}\le v^2_{\max}\) 的体积。
-
使用转圈法优化同余最短路
常规同余最短路写法是 SPFA,但可以使用转圈法优化。
将同余最短路看做一个环形dp。
逐个考虑物品 \((v_i,w_i)\),此刻转移图会分为 \(\gcd(v_i,v_1)\) 个环。
根据最短路,我们不会重复经过点多次,因此我们只需要从每个环的任意点开始转两圈更新即可,这样足以涵盖所有情况。
int M=a[1].first,C=a[1].second; for(int i=2;i<=n;++i){ int w=a[i].first,v=a[i].second; for(int j=0,lim=__gcd(w,M);j<lim;++j){ for(int c=0,t=j;c<2;c+=(j==t)){ int d=(t+w)/M; int nt=t+w-d*M; f[nt]=max(f[nt],f[t]+v-C*d);t=nt; } } }
完全背包可行性
选择物品个数
仍然用 \(v_i\) 表示。
引理:任意一个可能被表达出的 \(M\),其最多用到 \(\log M\) 个不同物品即可表达出 \(M\),且表达的最小字典序满足此性质。
证明:设一个选取方案 \(\overline{c}=(c_1,c_2\dots c_n)\),\(\sum c_iv_i=M\)。
将 \(\overline{c}\) 视作一个数组,进行字典序比对,则字典序最小的方案一定满足 \(\prod (c_i+1)\le M+1\)
反证法,如果 \(>M+1\),根据鸽巢原理,有总方案数 \(\prod (c_i+1)>M+1\),其方案体积和在 \([0,M]\), 则必然存在两个体积和相同的子方案 \(\overline{c_1},\overline{c_2}\) 同时二者不同时取同一个物品。
则 \(\overline{c}-\overline{c_1}+\overline{c_2}\) 与 \(\overline{c}-\overline{c_2}+\overline{c_1}\) 其中字典序更小的方案会比原方案字典序更小。(考虑两个子方案第一个不同的位置)。
因此若有 \(n\) 个物品,找出方案可以 \(O(m{n\choose \log n})\) 解决,如果 \(n\) 比较小,还可以由此做子集和算出每个物品子集是否可行。
引理:对于带价值的问题,同样满足存在一个价值最大的最小字典序方案满足此性质。
证明关键在于 \(\overline{c}-\overline{c_1}+\overline{c_2}\) 与 \(\overline{c}-\overline{c_2}+\overline{c_1}\) 两个方案的权值肯定与 \(\overline{c}\) 一个 $\ge $ 一个 \(\le\)。
若假定 \(\overline{c}\) 最大,则必然取 \(=\),则逐步调整即可证明。
多重背包可行性-bitset
设 \(n\) 个数 \(a_i\)。设 \(\sum_{i=1}^na_i=M\),则做 \([0,M]\) 的可行性背包时,可以做到 \(O(\frac{M\sqrt M}{w})\)。
相同大小物品分组,至多 \(O(\sqrt M)\) 组,接着二进制拆分,拆分后 \(O(\sqrt M\log n)\) 个物品和仍然是 \(M\),因此可以再次紧缩,重复操作即可。
另一个更优秀的做法是:从小到大在值域上扫描所有数字,若 \(i\) 出现了偶数次 \(c\),则取出两个,同时将 \(2i\) 出现次数加上 \(\frac{c-2}{2}\),如果是奇数次就取出一个,然后给 \(2i\) 加上 \(\frac{c-1}{2}\)。
可以证明最终会剩下 \(O(\sqrt M)\) 个数字。
01 背包可行性——单组,极大容量背包
给定 \(n\) 个物品 \(v_i\in [1,D]\),询问是否存在选取方案可以凑出 \(C\)。
\(O(nD)\)。
考虑方案的组成,答案一定可以组成为 \((l,r)\),也就是 \([1,l-1]\) 的物品全部取了,\([l,r]\) 的物品不一定取了,\([r+1,n]\) 的物品一定没取。
考虑从一个点 \(p\) 开始扩展,设 \(s_p=\sum_{i=1}^pv_i\) 是大于等于 \(C\) 中最小的 \(p\)。则一定满足 \(0\le s_p-C\le D\),从 \([p+1,p]\) 开始扩张,往左转移,存在不改变已经钦定的 \(p-1\) 用了的状态,以及重新钦定 \(p-1\) 没有用,分别的大小变化是 \(0,-v_{p-1}\),往右转移类似的,分别的大小变化是 \(0,v_{p+1}\)。
因此左转贡献非负,右转贡献非正,则 \((p+1,p,s_p-C)\),完全可以在第三维与 \(C\) 偏差不超过 \(D\) 的路径上转移到任意可行的 \((l,r,k)\)。在转移路程中,第三维是正数,就去左边找转移,否则去右边找转移。显然覆盖了所有可行路径,且第三维的有效值只有 \([C-D,C+D]\)。
考虑优化,可行性太浪费,注意到 \(0\) 的转移,也即 \((l,r,k)\) 可行则 \((l-1,r,k)\) 一定可行。
因此我们设 \(f_{r,k}\) 表示最大的 \(l\) 使得 \((l,r,k)\) 可行。
转移分为:
- \(f_{r,k}\leftarrow f_{r-1,k}\)
- \(f_{r,k+a_r}\leftarrow f_{r-1,k},k<C\)
- \(f_{r,k-a_l}\leftarrow l,l\le f_{r,k},k\ge C\)
第三类转移太烦,但注意到 \((r-1,k-a_l)\to (r,k-a_l)\) 的存在,则只需要对 \(l\in [f_{r-1,k}+1,f_{r,k}]\) 的 \(l\) 进行转移即可。则对于一个 \(k\),总枚举量 $
板题 abc221g
多项式优化背包可行性
P4389
01 背包
选一个随机数组 \(w_i\),相当于算:
的非零项。手算 \(\ln\) 之后用 \(O(n\log n)\) 的调和级数办法求和后 exp 回去。
完全背包
选一个随机数组 \(w_i\),相当于算:
仍然用调和级数求和后 exp。
这里有一个 powered by ddxrS 的小清新倍增
设容量上界 \(m\),设 \(H(z)\) 表示每个数值的存在状况。
考虑先对不大于 \(t=\lfloor\frac{m}{n}\rfloor\) 的数字暴力 dp 求出可行状态 \(F_{\lfloor\frac{m}{n}\rfloor}\)。
倍增,有:
\[F_{2^it-1}=F_{2^{i-1}t}\times F_{2^{i-1}t}\times H(z)(\bmod z^{2^{it}}) \]做完之后给系数非零位置重新赋权防止被卡。
多重背包
选一个随机数组 \(w\),相当于算
还是调和级数求和系数后 exp。
SMAWK 算法
有一个 \(n\times m\) 的矩阵,设 \(L_i\) 为每一行最小值所在的列位置,满足 \(L_i\le L_{i+1}\)。
在 \(O(n+m)\) 次询问矩阵点值得到 \(L\) 序列。
Reduce 操作
当 \(m>n\) 时,一定是存在列是无用的,reduce 操作通过删除无效列,将 \(m\) 减小到 \(\le n\)。
算法原理:有效列中,每一列作为行最小值的位置不会比上一列高。
维护一个 \(k\),初始 \(k=1\)。
- 若 \(a_{k,k}\ge a_{k,k+1}\),说明第 \(k\) 列无用,删去第 \(k\) 列,\(k\leftarrow \max(k-1,1)\)。
- 若 \(a_{k,k}<a_{k,k+1}\)
- \(k=n\),说明 \(k+1\) 不可能有用,将其删去。
- \(k<n\),将 \(k\) 加一,纳入考虑范围。
vector<int> Reduce(vector<int>X,vector<int>Y){
vector<int>nY;int p=0,sur=Y.size(),c=0;
while(p<Y.size()){
if(nY.empty()){
nY.push_back(Y[p]);c=0;++p;
continue;
}
if(ask(X[c],Y[p])<=ask(X[c],nY.back())){
nY.pop_back();--c;
continue;
}
if(c+1<X.size())++c,nY.push_back(Y[p]);
++p;
}
return nY;
}
分治策略
首先取出偶数行,递归。
根据 \(L\) 的性质,我们只需要枚举奇数行的答案,总量是 \(O(len)\) 的。
vector<int>SMAWK(vector<int>X,vector<int>Y){
Y=Reduce(X,Y);if(X.size()==1)return Y;
vector<int>nX;for(int i=0;i<X.size();i+=2)nX.push_back(X[i]);
vector<int>g=SMAWK(nX,Y);vector<int>f;
for(int i=0,lst=0;i<X.size();++i){
while(Y[lst]<g[i>>1])++lst;
if(i&1){
int v=ask(X[i],Y[lst]),p=lst;++lst;
if(lst<Y.size()&&(i+1==X.size()||Y[lst]<=g[i+1>>1])){
int k=ask(X[i],Y[lst]);
if(k<=v)v=k,p=lst;
++lst;
}
lst=p;f.push_back(Y[p]);
}
else f.push_back(g[i>>1]);
}
return f;
}
复杂度基本线性,懒得分析。
用途—决策单调性优化
对于一个数组 \(f\) 与 一个凸数组 \(g\) 做 \(\max,\min\) 卷积时,有决策单调性,这很显然。
构造矩阵 \(A_{i,j}=f_{i-j}+g_j\)。
则卷积完之后的数组相当于查询每一行最小值,应用 SMAWK 即可。
CF1423M。
完全背包最优化——小体积大总量单次询问
ABC221G。
一个很神奇的技巧。
给你 \(n\) 个数字 \(a_i\in [1,D]\),每个数字可以任意用,有其价值 \(v_i\),给你一个 \(C\),问你表达出 \(C\) 的最优方案价值如何。
\(C\) 很大,\(D\) 很小。考虑在 \(O(D^2\log C)\) 解决。
首先暴力 DP 出 \([1,5D]\) 的解,然后考虑递归求解 \(C\)。
对于任意足够接近 \(C\) 的方案,其与 \(C\) 偏差不超过 \(D\),因此考虑求出 \([C-D,C+D]\) 的最优方案价值。
分治下去,先算出 \([\frac{C}{2}-D,\frac{C}{2}+D]\) 的最优方案价值,然后暴力卷积得到。
分治到底层可以直接用暴力 DP 的解。

浙公网安备 33010602011771号