此优化针对以下计数问题:
n 件物品,背包容量为 m,第 i 件物品体积为 ai,求装满的方案数。(01背包)
n 种物品,背包容量为 m,第 i 件物品体积为 ai,数量无限,求装满的方案数。(完全背包)
n 种物品,背包容量为 m,第 i 件物品体积为 ai,数量为 bi,求装满的方案数。(多重背包)
(1≤n≤1e5,1≤m≤1e5,1≤ai,bi≤1e5)
不止求装满的方案数,用多项式优化后求装任意体积都是 O(mlogm)。
01背包计数
首先来看计数01背包的生成函数:
第 i 个物品:Gi(x)=1+xai
x0 项表示不选,xai 表示选,占体积为 ai。
那么刚好装满容量为 k 的背包的方案数为:G(x)[xk]=n∏i=1Gi(x)[xk]=(1+xa1)(1+xa2)...(1+xan)[xk]
这个式子看起来很好求,和我们之前遇到的另一个式子很像:(x+a1)(x+a2)...(x+an),这个式子就可以用分治NTT来求。
但是上面的式子次数太高了,如果用分治NTT来做,分治的最底层就已经爆炸了。
我们用一个小技巧,这种连乘的算式先取个对数 ln,就可以变成相加的形式,求和之后再 exp 回去就是原式:
G(x)=exp(ln(1+xa1)+ln(1+xa2)+...+ln(1+xan))
每个物品的式子都求一遍 ln 再挨个相加,这个复杂度仍然是平方的。
但是我们发现 G(x) 的每一个因式都只有两项,这个式子可以直接套用麦克劳林公式手算:ln(1+x)=x−x22+x33−x44+...+(−1)nxn+1n+1
把 x 换成 xa :ln(1+xa)=xa−x2a2+x3a3−x4a4+...+(−1)nx(n+1)an+1
我们发现在总容量为 m 的限制下,一个体积为 a 的物品式子中有用的项只有 ma 个,其余项为0。
我们记录每种体积的物品有几个,那么每一个不同的 a 只会算一次,∑ma 的复杂度是调和级数的 mlogm。于是我们就解决了这个问题。
完全背包计数
洛谷有个板子题:P4389 付公主的背包,想要代码的可以看这题的题解。
和01背包原理一样,将生成函数先取对数,相加后再 exp 回去。
只是现在每种物品有无限个,因此生成函数式子稍有不同:
Gi(x)=1+xai+x2ai+...+xnai+...
Gi(x)=∞∑j=0xj⋅ai
Gi(x)=11−xai
至于这个式子怎么转化的,那就是生成函数的基础了。
我们将这些式子取对数再相加:
ln(Gi(x))=ln(11−xai)=−ln(1−xai)=xai+x2ai2+x3ai3+x4ai4+...+x(n+1)an+1
所以几乎和01背包的式子一样。
多重背包计数
和前者一样,也是只有生成函数式子有不同。
第 i 种物品有 bi 个,因此生成函数有 bi+1 项:
Gi(x)=1+xai+x2ai+...+xbiai
Gi(x)=1−xbiai1−xai
取对数后分母取负,就和完全背包的式子形式一样了,注意分子在调和级数去重时要按照 a*b 的值。
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具