Skip to content

Commit 3d294d8

Browse files
authored
feat(dp): add monotonous queue opt (OI-wiki#1857)
feat(dp): add monotonous queue opt
2 parents 2455512 + bb13558 commit 3d294d8

File tree

4 files changed

+46
-48
lines changed

4 files changed

+46
-48
lines changed

docs/dp/knapsack.md

+19-12
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,13 @@ for (int i = 1; i <= n; i++)
8282

8383
可以考虑一个朴素的做法:对于第 $i$ 件物品,枚举其选了多少个来转移。这样做的时间复杂度是 $O(n^3)$ 的。
8484

85-
尽管这样看起来很蠢,我们还是写一下 dp 方程
85+
状态转移方程如下
8686

8787
$$
8888
f_{i,j}=\max_{k=0}^{+\infty}(f_{i-1,j-k\times w_i}+v_i\times k)
8989
$$
9090

91-
然而这样显然不够优秀,我们要对它进行优化。
92-
93-
可以发现,对于 $f_{i,j}$ ,只要通过 $f_{i,j-w_i}$ 转移就可以了。dp 方程为:
91+
考虑做一个简单的优化。可以发现,对于 $f_{i,j}$ ,只要通过 $f_{i,j-w_i}$ 转移就可以了。因此状态转移方程为:
9492

9593
$$
9694
f_{i,j}=\max(f_{i-1,j},f_{i,j-w_i}+v_i)
@@ -121,15 +119,21 @@ $$
121119

122120
## 多重背包
123121

124-
多重背包也是 0-1 背包的一个变式。与 0-1 背包的区别在于每种物品可以选 $k_i$ 次,而非 $1$ 次。
122+
多重背包也是 0-1 背包的一个变式。与 0-1 背包的区别在于每种物品 y 有 $k_i$ 个,而非 $1$ 个。
123+
124+
一个很朴素的想法就是:把“每种物品选 $k_i$ 次”等价转换为“有 $k_i$ 个相同的物品,每个物品选一次”。这样就转换成了一个 0-1 背包模型,套用上文所述的方法就可已解决。状态转移方程如下:
125125

126-
一个很朴素的想法就是:把“每种物品选 $k_i$ 次”等价转换为“有 $k_i$ 个相同的物品,每个物品选一次”。这样就转换成了一个 0-1 背包模型,套用上文所述的方法就可已解决。时间复杂度: $O(nW\sum k_i)$ ,可以轻松 TLE。
126+
$$
127+
f_{i,j}=\max_{k=0}^{k_i}(f_{i-1,j-k\times w_i}+v_i\times k)
128+
$$
127129

128-
虽然我们失败了,但是这个朴素的想法可以给我们的思路提供一些借鉴——我们可以把多重背包转化成 0-1 背包模型来求解
130+
时间复杂度 $O(nW\sum k_i)$
129131

130-
显然,复杂度中的 $O(nW)$ 部分无法再优化了,我们只能从 $O(\sum k_i)$ 处入手。
132+
### 二进制分组优化
131133

132-
为了表述方便,我们用 $A_{i,j}$ 代表第 $i$ 种物品拆分出的第 $j$ 个物品。
134+
考虑优化。我们仍考虑把多重背包转化成 0-1 背包模型来求解。
135+
136+
显然,复杂度中的 $O(nW)$ 部分无法再优化了,我们只能从 $O(\sum k_i)$ 处入手。为了表述方便,我们用 $A_{i,j}$ 代表第 $i$ 种物品拆分出的第 $j$ 个物品。
133137

134138
在朴素的做法中, $\forall j\le k_i$ , $A_{i,j}$ 均表示相同物品。那么我们效率低的原因主要在于我们进行了大量重复性的工作。举例来说,我们考虑了“同时选 $A_{i,1},A_{i,2}$ ”与“同时选 $A_{i,2},A_{i,3}$ ”这两个完全等效的情况。这样的重复性工作我们进行了许多次。那么优化拆分方式就成为了解决问题的突破口。
135139

@@ -146,7 +150,7 @@ $$
146150

147151
显然,通过上述拆分方式,可以表示任意 $\le k_i$ 个物品的等效选择方式。将每种物品按照上述方式拆分后,使用 0-1 背包的方法解决即可。
148152

149-
时间复杂度 $O(nW\sum\log k_i)$
153+
时间复杂度 $O(W\sum_{i=1}^n\log_2k_i)$
150154

151155
??? 二进制分组代码
152156
```cpp
@@ -165,8 +169,11 @@ $$
165169
}
166170
```
167171

168-
??? note "[「Luogu P1776」宝物筛选\_NOI 导刊 2010 提高(02)](https://www.luogu.org/problemnew/show/P1776)"
169-
题意概要:有 $n$ 种物品和一个容量为 $W$ 的背包,每种物品有 $m_v{i}$ 个,同时每个物品有两种属性:重量 $w_{i}$ 和价值 $v_{i}$ 。要求选若干个物品放入背包使背包中物品的总价值最大且背包中物品的总重量不超过背包的容量。有一点需要注意的是本题数据范围较大,情况较多。
172+
### 单调队列优化
173+
174+
[单调队列/单调栈优化](opt/monotonous-queue-stack.md)
175+
176+
习题: [「Luogu P1776」宝物筛选\_NOI 导刊 2010 提高(02)](https://www.luogu.org/problemnew/show/P1776)
170177

171178
## 混合背包
172179

docs/dp/opt/binary-knapsack.md

-35
This file was deleted.

docs/dp/opt/monotonous-queue-stack.md

+27
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,33 @@ author: TrisolarisHD, hsfzLZH1, Ir1d, greyqz, Anguei, billchenchina, Chrogeek, C
3131

3232
讲完了,让我们归纳一下单调队列优化动态规划问题的基本形态:当前状态的所有值可以从上一个状态的某个连续的段的值得到,要对这个连续的段进行 RMQ 操作,相邻状态的段的左右区间满足非降的关系。
3333

34+
## 单调队列优化多重背包
35+
36+
???+note "问题描述"
37+
你有 $n$ 个物品,每个物品重量为 $w_i$ ,价值为 $v_i$ ,数量为 $k_i$ 。你有一个承重上限为 $m$ 的背包,现在要求你在不超过重量上限的情况下选取价值和尽可能大的物品放入背包。求最大价值。
38+
39+
不了解背包 DP 的请先阅读 [背包 DP](../knapsack.md) 。设 $f_{i,j}$ 表示前 $i$ 个物品装入承重为 $j$ 的背包的最大价值,朴素的转移方程为
40+
41+
$$
42+
f_{i,j}=\max_{k=0}^{k_i}(f_{i-1,j-k\times w_i}+v_i\times k)
43+
$$
44+
45+
时间复杂度 $O(nW\sum k_i)$ 。
46+
47+
考虑优化 $f_i$ 的转移。为方便表述,设 $g_{x,y}=f_{i,x\times w_i+y},g'_{x,y}=f_{i-1,x\times w_i+y}$ ,则转移方程可以表示为:
48+
49+
$$
50+
g_{x,y}=\max_{k=0}^{k_i}(g'_{x-k,y}+v_i\times k)
51+
$$
52+
53+
设 $G_{x,y}=g'_{x,y}-v_i\times x$ 。则方程可以表示为:
54+
55+
$$
56+
g_{x,y}=\max_{k=0}^{k_i}(G_{x-k,y})+v_i\times x
57+
$$
58+
59+
这样就转化为一个经典的单调队列优化形式了。 $G_{x,y}$ 可以 $O(1)$ 计算,因此对于固定的 $y$ ,我们可以在 $O\left( \left\lfloor \dfrac{W}{w_i} \right\rfloor \right)$ 的时间内计算出 $g_{x,y}$ 。因此求出所有 $g_{x,y}$ 的复杂度为 $O\left( \left\lfloor \dfrac{W}{w_i} \right\rfloor \right)\times O(w_i)=O(W)$ 。这样转移的总复杂度就降为 $O(nW)$ 。
60+
3461
## 习题
3562

3663
[「Luogu P1886」滑动窗口](https://loj.ac/problem/10175)

mkdocs.yml

-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ nav:
144144
- 计数 DP: dp/count.md
145145
- 动态 DP: dp/dynamic.md
146146
- DP 优化:
147-
- 二进制分组解多重背包: dp/opt/binary-knapsack.md
148147
- 单调队列/单调栈优化: dp/opt/monotonous-queue-stack.md
149148
- 斜率优化: dp/opt/slope.md
150149
- 四边形不等式优化: dp/opt/quadrangle.md

0 commit comments

Comments
 (0)