/
atom.xml
266 lines (141 loc) · 324 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>WuDocs</title>
<subtitle>一站式学习网站</subtitle>
<link href="https://creating001.github.io/atom.xml" rel="self"/>
<link href="https://creating001.github.io/"/>
<updated>2023-06-02T11:47:41.490Z</updated>
<id>https://creating001.github.io/</id>
<author>
<name>划船全靠浪</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>图论之图的存储</title>
<link href="https://creating001.github.io/2023/05/31/graph-0-save/"/>
<id>https://creating001.github.io/2023/05/31/graph-0-save/</id>
<published>2023-05-31T08:26:32.000Z</published>
<updated>2023-06-02T11:47:41.490Z</updated>
<content type="html"><![CDATA[<h1>直接存边</h1><h2 id="方法">方法</h2><p>使用一个数组来存边,数组中的每个元素都包含一条边的起点与终点(带边权的图还包含边权)。(或者使用多个数组分别存起点,终点和边权。)</p><h2 id="代码实现">代码实现</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">Edge</span> {</span><br><span class="line"> <span class="type">int</span> u, v;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> N = <span class="number">1e5</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> n, m;</span><br><span class="line">Edge arr[N];</span><br><span class="line"><span class="type">bool</span> vis[N];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">find_edge</span><span class="params">(<span class="type">int</span> u, <span class="type">int</span> v)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= m; i++) {</span><br><span class="line"> <span class="keyword">if</span> (arr[i].u == u && arr[i].v == v) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> u)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (vis[u]) <span class="keyword">return</span>;</span><br><span class="line"> vis[u] = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= m; i++) {</span><br><span class="line"> <span class="keyword">if</span> (arr[i].u == i) <span class="built_in">dfs</span>(arr[i].v);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> cin >> n >> m;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= m; i++) cin >> arr[i].u >> arr[i].v;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度">复杂度</h2><p>查询是否存在某条边:<code>O(m)</code>。</p><p>遍历一个点的所有出边:<code>O(m)</code>。</p><p>遍历整张图:<code>O(nm)</code>。</p><p>空间复杂度:<code>O(m)</code>。</p><h2 id="应用">应用</h2><p>由于直接存边的遍历效率低下,一般不用于遍历图。</p><p>在 Kruskal 算法 中,由于需要将边按边权排序,需要直接存边。</p><p>在有的题目中,需要多次建图(如建一遍原图,建一遍反图),此时既可以使用多个其它数据结构来同时存储多张图,也可以将边直接存下来,需要重新建图时利用直接存下的边来建图。</p><h1>邻接矩阵</h1><h2 id="方法-2">方法</h2><p>使用一个二维数组 <code>arr</code> 来存边,其中 <code>arr[u][v]</code> 为 1 表示存在 <code>u</code> 到 <code>v</code> 的边,为 0 表示不存在。如果是带边权的图,可以在 <code>arr[u][v]</code> 中存储 <code>u</code> 到 <code>v</code> 的边的边权。</p><h2 id="代码实现-2">代码实现</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> N = <span class="number">1e5</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> n, m;</span><br><span class="line"><span class="type">int</span> arr[N][N];</span><br><span class="line"><span class="type">int</span> vis[N];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">find_edge</span><span class="params">(<span class="type">int</span> u, <span class="type">int</span> v)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> arr[u][v] != <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> u)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (vis[u]) <span class="keyword">return</span>;</span><br><span class="line"> vis[u] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= n; i++) {</span><br><span class="line"> <span class="keyword">if</span> (arr[u][i]) <span class="built_in">dfs</span>(i);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> cin >> n >> m;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= m; i++) {</span><br><span class="line"> <span class="type">int</span> u, v, w;</span><br><span class="line"> cin >> u >> v >> w;</span><br><span class="line"> arr[u][v] = w;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度-2">复杂度</h2><p>查询是否存在某条边:<code>O(1)</code>。</p><p>遍历一个点的所有出边:<code>O(n)</code>。</p><p>遍历整张图:<code>O(n^2)</code>。</p><p>空间复杂度:<code>O(n^2)</code>。</p><h2 id="应用-2">应用</h2><p>邻接矩阵只适用于没有重边(或重边可以忽略)的情况。</p><p>其最显著的优点是可以 <code>O(1)</code> 查询一条边是否存在。</p><p>由于邻接矩阵在稀疏图上效率很低(尤其是在点数较多的图上,空间无法承受),所以一般只会在稠密图上使用邻接矩阵。</p><h1>邻接表</h1><h2 id="方法-3">方法</h2><p>使用一个支持动态增加元素的数据结构构成的数组,如 <code>vector<int> arr[n + 1]</code> 来存边,其中 <code>adj[u]</code> 存储的是点 <code>u</code> 的所有出边的相关信息(终点、边权等)。</p><h3 id="代码实现-3">代码实现</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> N = <span class="number">1e5</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> n, m;</span><br><span class="line">vector<<span class="type">int</span>> arr[N];</span><br><span class="line"><span class="type">int</span> vis[N];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">find_edge</span><span class="params">(<span class="type">int</span> u, <span class="type">int</span> v)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span> to : arr[u]) <span class="keyword">if</span> (to == v) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> u)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (vis[u]) <span class="keyword">return</span>;</span><br><span class="line"> vis[u] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span> v : arr[u]) {</span><br><span class="line"> <span class="built_in">dfs</span>(v);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> cin >> n >> m;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i <= m; i++) {</span><br><span class="line"> <span class="type">int</span> u, v;</span><br><span class="line"> cin >> u >> v;</span><br><span class="line"> arr[u].<span class="built_in">emplace_back</span>(v);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度-3">复杂度</h2><p>查询是否存在 u 到 v 的边:<code>O(d^+(u))</code>(如果事先进行了排序就可以使用 二分查找 做到 <code>O(log(d^+(u)))</code>)。</p><p>遍历点 u 的所有出边:<code>O(d^+(u))</code>。</p><p>遍历整张图:<code>O(n+m)</code>。</p><p>空间复杂度:<code>O(m)</code>。</p><h2 id="应用-3">应用</h2><p>存各种图都很适合,除非有特殊需求(如需要快速查询一条边是否存在,且点数较少,可以使用邻接矩阵)。</p><p>尤其适用于需要对一个点的所有出边进行排序的场合。</p><h1>链式前向星</h1><h2 id="方法-4">方法</h2><p>本质上是用链表实现的邻接表。</p><h2 id="代码实现-4">代码实现</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> N = <span class="number">1e5</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Edge</span> {</span><br><span class="line"> <span class="type">int</span> to;</span><br><span class="line"> <span class="type">int</span> w;</span><br><span class="line"> <span class="type">int</span> next;</span><br><span class="line">} arr[N << <span class="number">1</span>];</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> n, m;</span><br><span class="line"><span class="type">int</span> head[N], index;</span><br><span class="line"><span class="type">int</span> vis[N];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">init</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i < N; i++) {</span><br><span class="line"> head[i] = <span class="number">-1</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">void</span> <span class="title">add_edge</span><span class="params">(<span class="type">int</span> u, <span class="type">int</span> v, <span class="type">int</span> w)</span> </span>{</span><br><span class="line"> arr[index].to = v;</span><br><span class="line"> arr[index].w = w;</span><br><span class="line"> arr[index].next = head[u];</span><br><span class="line"> head[u] = index++;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> u)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (vis[u]) <span class="keyword">return</span>;</span><br><span class="line"> vis[u] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = head[u]; i != <span class="number">-1</span>; i = arr[i].next) {</span><br><span class="line"> <span class="built_in">dfs</span>(arr[i].to);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> cin >> n >> m;</span><br><span class="line"> <span class="built_in">init</span>();</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= m; i++) {</span><br><span class="line"> <span class="type">int</span> u, v, w;</span><br><span class="line"> cin >> u >> v >> w;</span><br><span class="line"> <span class="built_in">add_edge</span>(u, v, w);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度-4">复杂度</h2><p>查询是否存在 u 到 v 的边:<code>O(d^+(u))</code>。</p><p>遍历点 u 的所有出边:<code>O(d^+(u))</code>。</p><p>遍历整张图:<code>O(n+m)</code>。</p><p>空间复杂度:<code>O(m)</code>。</p><h2 id="应用-4">应用</h2><p>存各种图都很适合,但不能快速查询一条边是否存在,也不能方便地对一个点的出边进行排序。</p><p>优点是边是带编号的,有时会非常有用,而且如果 cnt 的初始值为奇数,存双向边时 i ^ 1 即是 i 的反边(常用于 网络流)。</p>]]></content>
<summary type="html"><h1>直接存边</h1>
<h2 id="方法">方法</h2>
<p>使用一个数组来存边,数组中的每个元素都包含一条边的起点与终点(带边权的图还包含边权)。(或者使用多个数组分别存起点,终点和边权。)</p>
<h2 id="代码实现">代码实现</h2>
<figure c</summary>
<category term="图论" scheme="https://creating001.github.io/categories/%E5%9B%BE%E8%AE%BA/"/>
<category term="图论" scheme="https://creating001.github.io/tags/%E5%9B%BE%E8%AE%BA/"/>
<category term="图的存储" scheme="https://creating001.github.io/tags/%E5%9B%BE%E7%9A%84%E5%AD%98%E5%82%A8/"/>
</entry>
<entry>
<title>动态规划之数位dp</title>
<link href="https://creating001.github.io/2023/05/28/dp-6-digit/"/>
<id>https://creating001.github.io/2023/05/28/dp-6-digit/</id>
<published>2023-05-28T12:20:58.000Z</published>
<updated>2023-06-02T11:43:09.538Z</updated>
<content type="html"><![CDATA[<h1>引入</h1><p>数位是指把一个数字按照个、十、百、千等等一位一位地拆开,关注它每一位上的数字。如果拆的是十进制数,那么每一位数字都是 0~9,其他进制可类比十进制。</p><p>数位 DP:用来解决一类特定问题,这种问题比较好辨认,一般具有这几个特征:</p><ol><li>要求统计满足一定条件的数的数量(即,最终目的为计数);</li><li>这些条件经过转化后可以使用「数位」的思想去理解和判断;</li><li>输入会提供一个数字区间(有时也只提供上界)来作为统计的限制;</li><li>上界很大(比如 <code>10^18</code>),暴力枚举验证会超时。</li></ol><h1>数位 DP 的基本原理</h1><p>考虑人类计数的方式,最朴素的计数就是从小到大开始依次加一。但我们发现对于位数比较多的数,这样的过程中有许多重复的部分。例如,从 7000 数到 7999、从 8000 数到 8999、和从 9000 数到 9999 的过程非常相似,它们都是后三位从 000 变到 999,不一样的地方只有千位这一位,所以我们可以把这些过程归并起来,将这些过程中产生的计数答案也都存在一个通用的数组里。此数组根据题目具体要求设置状态,用递推或 DP 的方式进行状态转移。</p><p>数位 DP 中通常会利用常规计数问题技巧,比如把一个区间内的答案拆成两部分相减。</p><p>那么有了通用答案数组,接下来就是统计答案。统计答案可以选择记忆化搜索,也可以选择循环迭代递推。为了不重不漏地统计所有不超过上限的答案,要从高到低枚举每一位,再考虑每一位都可以填哪些数字,最后利用通用答案数组统计答案。</p><h1>典型例题</h1><h2 id="数字计数"><a href="https://www.luogu.com.cn/problem/P2602">数字计数</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> LL = <span class="type">long</span> <span class="type">long</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> N = <span class="number">15</span>;</span><br><span class="line">LL dp[N], ten[N];</span><br><span class="line">LL ans1[N], ans2[N];</span><br><span class="line">LL a[N];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">solve</span><span class="params">(LL n, LL* ans)</span> </span>{</span><br><span class="line"> LL temp = n;</span><br><span class="line"> <span class="type">int</span> len = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (n) a[++len] = n % <span class="number">10</span>, n /= <span class="number">10</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = len; i >= <span class="number">1</span>; i--) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < <span class="number">10</span>; j++) ans[j] += dp[i - <span class="number">1</span>] * a[i];</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < a[i]; j++) ans[j] += ten[i - <span class="number">1</span>];</span><br><span class="line"> temp -= ten[i - <span class="number">1</span>] * a[i];</span><br><span class="line"> ans[a[i]] += temp + <span class="number">1</span>;</span><br><span class="line"> ans[<span class="number">0</span>] -= ten[i - <span class="number">1</span>];</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> LL l, r;</span><br><span class="line"> cin >> l >> r;</span><br><span class="line"> ten[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= <span class="number">13</span>; i++) {</span><br><span class="line"> dp[i] = dp[i - <span class="number">1</span>] * <span class="number">10</span> + ten[i - <span class="number">1</span>];</span><br><span class="line"> ten[i] = ten[i - <span class="number">1</span>] * <span class="number">10</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">solve</span>(l - <span class="number">1</span>, ans1);</span><br><span class="line"> <span class="built_in">solve</span>(r, ans2);</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">10</span>; i++) cout << ans2[i] - ans1[i] << <span class="string">" "</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="不要62"><a href="https://www.luogu.com.cn/problem/U94702">不要62</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure><h2 id="windy-数"><a href="https://www.luogu.com.cn/problem/P2657">windy 数</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure><h2 id="Mirror-Number"><a href="https://www.luogu.com.cn/problem/SP10649">Mirror Number</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure><h2 id="数数"><a href="https://www.luogu.com.cn/problem/P3311">数数</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure><h2 id="同类分布"><a href="https://www.luogu.com.cn/problem/P4127">同类分布</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure><h2 id="萌数"><a href="https://www.luogu.com.cn/problem/P3413">萌数</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure><h2 id="Valley-Numer"><a href="https://vjudge.net/problem/HDU-6148">Valley Numer</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure><h2 id="Beautiful-numbers"><a href="https://www.luogu.com.cn/problem/CF55D">Beautiful numbers</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure><h2 id="Magic-Numbers"><a href="https://www.luogu.com.cn/problem/CF628D">Magic Numbers</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h1>引入</h1>
<p>数位是指把一个数字按照个、十、百、千等等一位一位地拆开,关注它每一位上的数字。如果拆的是十进制数,那么每一位数字都是 0~9,其他进制可类比十进制。</p>
<p>数位 DP:用来解决一类特定问题,这种问题比较好辨认,一般具有这几个特征:</p>
<</summary>
<category term="算法" scheme="https://creating001.github.io/categories/%E7%AE%97%E6%B3%95/"/>
<category term="动态规划" scheme="https://creating001.github.io/categories/%E7%AE%97%E6%B3%95/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="动态规划" scheme="https://creating001.github.io/tags/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="数位dp" scheme="https://creating001.github.io/tags/%E6%95%B0%E4%BD%8Ddp/"/>
</entry>
<entry>
<title>动态规划之状压dp</title>
<link href="https://creating001.github.io/2023/05/25/dp-5-state/"/>
<id>https://creating001.github.io/2023/05/25/dp-5-state/</id>
<published>2023-05-25T12:20:58.000Z</published>
<updated>2023-06-02T11:42:24.640Z</updated>
<content type="html"><![CDATA[<h1>定义</h1><p>状压 DP 是动态规划的一种,通过将状态压缩为整数来达到优化转移的目的。</p><h1>引入</h1><h2 id="题目描述">题目描述</h2><p><a href="https://www.luogu.com.cn/problem/P1896">互不侵犯</a></p><p>在 <code>N*N</code> 的棋盘里面放 <code>K</code> 个国王(<code>1 <= N <= 9, 1 <= K <= N * N</code>),使他们互不攻击,共有多少种摆放方案。</p><p>国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共 8 个格子。</p><h2 id="思路分析">思路分析</h2><p>设 <code>dp(i,j,l)</code> 表示前 i 行,第 i 行的状态为 j,且棋盘上已经放置 l 个国王时的合法方案数。</p><p>对于编号为 j 的状态,我们用二进制整数 <code>state(j)</code> 表示国王的放置情况,<code>state(j)</code> 的某个二进制位为 0 表示对应位置不放国王,为 1 表示在对应位置上放置国王;用 <code>stateCount(j)</code> 表示该状态的国王个数,即二进制数 <code>state(j)</code> 中 1 的个数。例如,如下图所示的状态可用二进制数 100101 来表示(棋盘左边对应二进制低位),则有 <code>state(j)=100101</code>, <code>stateCount(j)=3</code>。</p><p><img src="https://oi-wiki.org/dp/images/SCOI2005-%E4%BA%92%E4%B8%8D%E4%BE%B5%E7%8A%AF.png" alt=""></p><p>设当前行的状态为 j,上一行的状态为 k,在保证当前行和上一行不冲突的前提下,枚举所有可能的 k 进行转移,转移方程:</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>i</mi><mo separator="true">,</mo><mi>j</mi><mo separator="true">,</mo><mi>l</mi><mo stretchy="false">)</mo><mo>=</mo><mo>∑</mo><mi>f</mi><mo stretchy="false">(</mo><mi>i</mi><mo>−</mo><mn>1</mn><mo separator="true">,</mo><mi>k</mi><mo separator="true">,</mo><mi>l</mi><mo>−</mo><mi>s</mi><mi>t</mi><mi>a</mi><mo stretchy="false">(</mo><mi>j</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">f(i,j,l) = \sum f(i-1,k,l-sta(j)) </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mopen">(</span><span class="mord mathnormal">i</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05724em;">j</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.6em;vertical-align:-0.55em;"></span><span class="mop op-symbol large-op" style="position:relative;top:0em;">∑</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mopen">(</span><span class="mord mathnormal">i</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord">1</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">s</span><span class="mord mathnormal">t</span><span class="mord mathnormal">a</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.05724em;">j</span><span class="mclose">))</span></span></span></span></span></p><h2 id="题解代码">题解代码</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> state[<span class="number">2005</span>];</span><br><span class="line"><span class="type">int</span> stateCount[<span class="number">2005</span>];</span><br><span class="line"><span class="type">long</span> <span class="type">long</span> dp[<span class="number">10</span>][<span class="number">2005</span>][<span class="number">105</span>];</span><br><span class="line"><span class="type">int</span> stateCnt = <span class="number">0</span>;</span><br><span class="line"><span class="type">int</span> N, K;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> s, <span class="type">int</span> num, <span class="type">int</span> cur)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (cur >= N) {</span><br><span class="line"> state[++stateCnt] = s;</span><br><span class="line"> stateCount[stateCnt] = num;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">dfs</span>(s, num, cur + <span class="number">1</span>);</span><br><span class="line"> <span class="built_in">dfs</span>(s + (<span class="number">1</span> << cur), num + <span class="number">1</span>, cur + <span class="number">2</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">bool</span> <span class="title">isCompatible</span><span class="params">(<span class="type">int</span> i, <span class="type">int</span> j)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (state[i] & state[j]) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">if</span> ((state[i] << <span class="number">1</span>) & state[j]) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">if</span> (state[i] & (state[j] << <span class="number">1</span>)) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> cin >> N >> K;</span><br><span class="line"> <span class="built_in">dfs</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= stateCnt; i++) dp[<span class="number">1</span>][i][stateCount[i]] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">2</span>; i <= N; i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j <= stateCnt; j++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">1</span>; k <= stateCnt; k++) {</span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">isCompatible</span>(j, k)) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> l = stateCount[j]; l <= K; l++) {</span><br><span class="line"> dp[i][j][l] += dp[i - <span class="number">1</span>][k][l - stateCount[j]];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="type">long</span> <span class="type">long</span> ans = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= stateCnt; i++) ans += dp[N][i][K];</span><br><span class="line"> cout << ans << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1>典型例题</h1><h2 id="最短Hamilton路径"><a href="https://www.acwing.com/problem/content/93/">最短Hamilton路径</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">1</span> << <span class="number">20</span>;</span><br><span class="line"><span class="type">int</span> dp[maxN][<span class="number">25</span>];</span><br><span class="line"><span class="type">int</span> g[<span class="number">25</span>][<span class="number">25</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">int</span> N;</span><br><span class="line"> cin >> N;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < N; i++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < N; j++)</span><br><span class="line"> cin >> g[i][j];</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0x3f</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> dp[<span class="number">1</span>][<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">2</span>; i < (<span class="number">1</span> << N); i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < N; j++) {</span><br><span class="line"> <span class="keyword">if</span> ((i >> j) & <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">0</span>; k < N; k++) {</span><br><span class="line"> <span class="keyword">if</span> ((i >> k) & <span class="number">1</span> && k != j) {</span><br><span class="line"> dp[i][j] = <span class="built_in">min</span>(dp[i][j], dp[i ^ (<span class="number">1</span> << j)][k] + g[k][j]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << dp[(<span class="number">1</span> << N) - <span class="number">1</span>][N - <span class="number">1</span>];</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="关灯问题II"><a href="https://www.luogu.com.cn/problem/P2622">关灯问题II</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> a[<span class="number">105</span>][<span class="number">15</span>];</span><br><span class="line"><span class="type">int</span> vis[<span class="number">1</span> << <span class="number">10</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">node</span> {</span><br><span class="line"> <span class="type">int</span> state;</span><br><span class="line"> <span class="type">int</span> step;</span><br><span class="line"> <span class="built_in">node</span>(<span class="type">int</span> state, <span class="type">int</span> step) : <span class="built_in">state</span>(state), <span class="built_in">step</span>(step) {}</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">int</span> N, M;</span><br><span class="line"> cin >> N >> M;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < M; i++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < N; j++)</span><br><span class="line"> cin >> a[i][j];</span><br><span class="line"> queue<node> q;</span><br><span class="line"> q.<span class="built_in">emplace</span>((<span class="number">1</span> << N) - <span class="number">1</span>, <span class="number">0</span>);</span><br><span class="line"> vis[(<span class="number">1</span> << N) - <span class="number">1</span>] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">while</span> (!q.<span class="built_in">empty</span>()) {</span><br><span class="line"> <span class="keyword">auto</span> cur = q.<span class="built_in">front</span>();</span><br><span class="line"> q.<span class="built_in">pop</span>();</span><br><span class="line"> <span class="keyword">if</span> (cur.state == <span class="number">0</span>) {</span><br><span class="line"> cout << cur.step << endl;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < M; i++) {</span><br><span class="line"> <span class="type">int</span> s = cur.state;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < N; j++) {</span><br><span class="line"> <span class="keyword">if</span> (a[i][j] == <span class="number">1</span> && s & (<span class="number">1</span> << j)) s ^= (<span class="number">1</span> << j);</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (a[i][j] == <span class="number">-1</span> && !(s & (<span class="number">1</span> << j))) s |= (<span class="number">1</span> << j);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (!vis[s]) {</span><br><span class="line"> q.<span class="built_in">emplace</span>(s, cur.step + <span class="number">1</span>);</span><br><span class="line"> vis[s] = <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << <span class="number">-1</span> << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="炮兵阵地"><a href="https://www.luogu.com.cn/problem/P2704">炮兵阵地</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> N = <span class="number">110</span>, M = <span class="number">10</span>, S = <span class="number">1</span> << M;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> n, m;</span><br><span class="line"><span class="type">int</span> g[N];</span><br><span class="line"><span class="type">int</span> f[<span class="number">2</span>][S][S];</span><br><span class="line"></span><br><span class="line">vector<<span class="type">int</span>> state;</span><br><span class="line">vector<<span class="type">int</span>> stateCnt;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">check</span><span class="params">(<span class="type">int</span> s)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < m; i++)</span><br><span class="line"> <span class="keyword">if</span> (s >> i & <span class="number">1</span> && ((s >> (i + <span class="number">1</span>) & <span class="number">1</span>) || (s >> (i + <span class="number">2</span>) & <span class="number">1</span>)))</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> cin >> n >> m;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < n; i++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < m; j++) {</span><br><span class="line"> <span class="type">char</span> ch;</span><br><span class="line"> cin >> ch;</span><br><span class="line"> <span class="keyword">if</span> (ch == <span class="string">'H'</span>) g[i] |= <span class="number">1</span> << j;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">1</span> << m; i++) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">check</span>(i)) {</span><br><span class="line"> state.<span class="built_in">emplace_back</span>(i);</span><br><span class="line"> stateCnt.<span class="built_in">emplace_back</span>(__builtin_popcount(i));</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < n + <span class="number">2</span>; i++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < state.<span class="built_in">size</span>(); j++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">0</span>; k < state.<span class="built_in">size</span>(); k++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> u = <span class="number">0</span>; u < state.<span class="built_in">size</span>(); u++) {</span><br><span class="line"> <span class="type">int</span> a = state[j], b = state[k], c = state[u];</span><br><span class="line"> <span class="keyword">if</span> (a & b || a & c || b & c) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">if</span> (g[i] & c) <span class="keyword">continue</span>;</span><br><span class="line"> f[i & <span class="number">1</span>][k][u] = <span class="built_in">max</span>(f[i & <span class="number">1</span>][k][u], f[i - <span class="number">1</span> & <span class="number">1</span>][j][k] + stateCnt[u]);</span><br><span class="line"> }</span><br><span class="line"> cout << f[n + <span class="number">1</span> & <span class="number">1</span>][<span class="number">0</span>][<span class="number">0</span>] << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Corn-Fields-G"><a href="https://www.luogu.com.cn/problem/P1879">Corn Fields G</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">12</span>, maxS = <span class="number">1</span> << maxN;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> mod = <span class="number">1e8</span>;</span><br><span class="line"><span class="type">int</span> g[maxN];</span><br><span class="line"><span class="type">int</span> dp[maxN][maxS];</span><br><span class="line">vector<<span class="type">int</span>> state;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> M, N;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> s, <span class="type">int</span> cur)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (cur >= N) {</span><br><span class="line"> state.<span class="built_in">emplace_back</span>(s);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">dfs</span>(s, cur + <span class="number">1</span>);</span><br><span class="line"> <span class="built_in">dfs</span>(s | (<span class="number">1</span> << cur), cur + <span class="number">2</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> cin >> M >> N;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < M; i++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < N; j++) {</span><br><span class="line"> <span class="type">int</span> num;</span><br><span class="line"> cin >> num;</span><br><span class="line"> num = <span class="number">1</span> - num;</span><br><span class="line"> g[i] |= num << j;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">dfs</span>(<span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i : state) <span class="keyword">if</span> ((i & g[<span class="number">0</span>]) == <span class="number">0</span>) dp[<span class="number">0</span>][i] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i < M; i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < state.<span class="built_in">size</span>(); j++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">0</span>; k < state.<span class="built_in">size</span>(); k++) {</span><br><span class="line"> <span class="type">int</span> u = state[j], v = state[k];</span><br><span class="line"> <span class="keyword">if</span> (v & u || v & g[i]) <span class="keyword">continue</span>;</span><br><span class="line"> dp[i][v] = (dp[i][v] + dp[i - <span class="number">1</span>][u]) % mod;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="type">int</span> ans = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i : state) ans = (ans + dp[M - <span class="number">1</span>][i]) % mod;</span><br><span class="line"> cout << ans << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="中国象棋"><a href="https://www.luogu.com.cn/problem/P2051">中国象棋</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> LL = <span class="type">long</span> <span class="type">long</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> mod = <span class="number">9999973</span>;</span><br><span class="line"></span><br><span class="line">LL dp[<span class="number">105</span>][<span class="number">105</span>][<span class="number">105</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">inline</span> LL <span class="title">C</span><span class="params">(<span class="type">int</span> m)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> (m * (m - <span class="number">1</span>) / <span class="number">2</span>) % mod;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">int</span> n, m;</span><br><span class="line"> cin >> n >> m;</span><br><span class="line"> dp[<span class="number">0</span>][<span class="number">0</span>][<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= n; i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j <= m; j++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">0</span>; k <= m - j; k++) {</span><br><span class="line"> dp[i][j][k] = (dp[i][j][k] + dp[i - <span class="number">1</span>][j][k]) % mod;</span><br><span class="line"> <span class="keyword">if</span> (j >= <span class="number">1</span>) dp[i][j][k] += dp[i - <span class="number">1</span>][j - <span class="number">1</span>][k] * (m - (j - <span class="number">1</span>) - k);</span><br><span class="line"> <span class="keyword">if</span> (k >= <span class="number">1</span>) dp[i][j][k] += dp[i - <span class="number">1</span>][j + <span class="number">1</span>][k - <span class="number">1</span>] * (j + <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">if</span> (j >= <span class="number">2</span>) dp[i][j][k] += dp[i - <span class="number">1</span>][j - <span class="number">2</span>][k] * <span class="built_in">C</span>(m - (j - <span class="number">2</span>) - k);</span><br><span class="line"> <span class="keyword">if</span> (k >= <span class="number">1</span>) dp[i][j][k] += dp[i - <span class="number">1</span>][j][k - <span class="number">1</span>] * j % mod * (m - j - (k - <span class="number">1</span>));</span><br><span class="line"> <span class="keyword">if</span> (k >= <span class="number">2</span>) dp[i][j][k] += dp[i - <span class="number">1</span>][j + <span class="number">2</span>][k - <span class="number">2</span>] * <span class="built_in">C</span>(j + <span class="number">2</span>) % mod;</span><br><span class="line"> dp[i][j][k] %= mod;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> LL ans = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i <= m; i++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j <= m - i; j++)</span><br><span class="line"> ans += dp[n][i][j] % mod;</span><br><span class="line"> cout << ans % mod << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="一双木棋-chess"><a href="https://www.luogu.com.cn/problem/P4363">一双木棋 chess</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h1>定义</h1>
<p>状压 DP 是动态规划的一种,通过将状态压缩为整数来达到优化转移的目的。</p>
<h1>引入</h1>
<h2 id="题目描述">题目描述</h2>
<p><a href="https://www.luogu.com.cn/problem/P18</summary>
<category term="算法" scheme="https://creating001.github.io/categories/%E7%AE%97%E6%B3%95/"/>
<category term="动态规划" scheme="https://creating001.github.io/categories/%E7%AE%97%E6%B3%95/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="动态规划" scheme="https://creating001.github.io/tags/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="状压dp" scheme="https://creating001.github.io/tags/%E7%8A%B6%E5%8E%8Bdp/"/>
</entry>
<entry>
<title>动态规划之树形dp</title>
<link href="https://creating001.github.io/2023/05/22/dp-4-tree/"/>
<id>https://creating001.github.io/2023/05/22/dp-4-tree/</id>
<published>2023-05-21T17:35:30.000Z</published>
<updated>2023-06-02T11:42:05.096Z</updated>
<content type="html"><![CDATA[<h1>概括</h1><p>树形 DP,即在树上进行的 DP。由于树固有的递归性质,树形 DP 一般都是递归进行的。</p><h1>基础</h1><h2 id="没有上司的舞会"><a href="https://www.luogu.com.cn/problem/P1352">没有上司的舞会</a></h2><p>某大学有 <code>n</code> 个职员,编号为 <code>1 --- N</code>。他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数 <code>Ai</code>,但是呢,如果某个职员的直接上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。</p><h3 id="思路分析">思路分析</h3><p>我们设 <code>f(i,0/1)</code> 代表以 <code>i</code> 为根的子树的最优解(第二维的值为 0 代表 i 不参加舞会的情况,1 代表 i 参加舞会的情况)。</p><p>对于每个状态,都存在两种决策(其中下面的 x 都是 i 的儿子):</p><ol><li>上司不参加舞会时,下属可以参加,也可以不参加,此时有 <code>f(i,0) = sum{max{f(x,1),f(x,0)}}</code>。</li><li>上司参加舞会时,下属都不会参加,此时有 <code>f(i,1) = sum{f(x,0)} + Ai</code>。</li></ol><p>我们可以通过 DFS,在返回上一层时更新当前结点的最优解。</p><h3 id="题解代码">题解代码</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">6e3</span> + <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line">vector<<span class="type">int</span>> arr[maxN];</span><br><span class="line"><span class="type">int</span> happy[maxN];</span><br><span class="line"><span class="type">int</span> vis[maxN];</span><br><span class="line"><span class="type">int</span> dp[maxN][<span class="number">2</span>];</span><br><span class="line"><span class="type">int</span> isRoot[maxN];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> s)</span> </span>{</span><br><span class="line"> vis[s] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span> u : arr[s]) {</span><br><span class="line"> <span class="keyword">if</span> (vis[u]) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="built_in">dfs</span>(u);</span><br><span class="line"> dp[s][<span class="number">1</span>] += dp[u][<span class="number">0</span>];</span><br><span class="line"> dp[s][<span class="number">0</span>] += <span class="built_in">max</span>(dp[u][<span class="number">0</span>], dp[u][<span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line"> dp[s][<span class="number">1</span>] += happy[s];</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">int</span> N;</span><br><span class="line"> cin >> N;</span><br><span class="line"> <span class="built_in">memset</span>(vis, <span class="number">0</span>, <span class="built_in">sizeof</span>(vis));</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="built_in">memset</span>(isRoot, <span class="number">1</span>, <span class="built_in">sizeof</span>(isRoot));</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) cin >> happy[i];</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i < N; i++) {</span><br><span class="line"> <span class="type">int</span> k, l;</span><br><span class="line"> cin >> l >> k;</span><br><span class="line"> isRoot[l] = <span class="number">0</span>;</span><br><span class="line"> arr[k].<span class="built_in">emplace_back</span>(l);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) {</span><br><span class="line"> <span class="keyword">if</span> (isRoot[i]) {</span><br><span class="line"> <span class="built_in">dfs</span>(i);</span><br><span class="line"> cout << <span class="built_in">max</span>(dp[i][<span class="number">0</span>], dp[i][<span class="number">1</span>]) << endl;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Computer"><a href="https://vjudge.net/problem/HDU-2196">Computer</a></h2><p>题意: 给出边有权的一棵树,求离每个节点最远的点的距离</p><h3 id="思路分析-2">思路分析</h3><p>求一个树中所有节点能到达的最远距离。要用两个dfs。</p><p>首先第一个dfs求出所有每个节点i在其子树中的正向最大距离 <code>dist[i][0]</code> 和正向次大距离 <code>dist[i][1]</code> (如果i节点在子树中最大距离经过了2号儿子,那么次大距离就是不经过2号儿子的最大距离)。并且还要标记 <code>longest[i]=j</code> 表示节点i在其子树中的最大距离经过了节点j(即j是i的一个儿子)。</p><p>由上步我们获得了正向最大距离,正向次大距离和最大距离的儿子节点标记。画图可以知道我们建立的这棵树,i节点的最远距离只有两种选择:i节点所在子树的最大距离,或者i节点连接它的父节点所能到达的最大距离。(即前者往下走,后者先往上走之后很可能也往下走)</p><p>所以我们只要求出反向最大距离<code>dist[i][2]</code>(即i节点往它的父节点走所能到达的最大距离)就可以知道i节点在整个树中能走的最大距离了。</p><p><code>dist[i][2]</code>求法:i节点往它的父节j点走,如果它的父节点的正向最大距离不经过i的话,那么<code>dist[i][2]</code>要不就是它父节点的反向最大距离+<code>W[i][j]</code>要不就是它父节点的正向最大距离+ <code>W[i][j]</code>.</p><p>如果它的父节点的正向最大距离经过i的话,那么<code>dist[i][2]</code>要不就是它父节点的反向最大距离+<code>W[i][j]</code>要不就是它父节点的正向次大距离+ <code>W[i][j]</code>.</p><p>上面就是dfs2要求的值。最终<code>f[i] = max(dist[i][0],dist[i][2])</code></p><h3 id="代码实现">代码实现</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">Edge</span> {</span><br><span class="line"> <span class="type">int</span> to;</span><br><span class="line"> <span class="type">int</span> val;</span><br><span class="line"> <span class="built_in">Edge</span>(<span class="type">int</span> to, <span class="type">int</span> val) : <span class="built_in">to</span>(to), <span class="built_in">val</span>(val) {}</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">1e4</span> + <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line">vector<Edge> arr[maxN];</span><br><span class="line"><span class="type">int</span> dp[maxN][<span class="number">3</span>];</span><br><span class="line"><span class="type">int</span> longest[maxN];</span><br><span class="line"><span class="type">int</span> vis[maxN];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs1</span><span class="params">(<span class="type">int</span> s, <span class="type">int</span> fa)</span> </span>{</span><br><span class="line"> vis[s] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span>& e : arr[s]) {</span><br><span class="line"> <span class="keyword">if</span> (e.to == fa) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">if</span> (vis[e.to]) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="built_in">dfs1</span>(e.to, s);</span><br><span class="line"> <span class="keyword">if</span> (dp[s][<span class="number">0</span>] < dp[e.to][<span class="number">0</span>] + e.val) {</span><br><span class="line"> longest[s] = e.to;</span><br><span class="line"> dp[s][<span class="number">1</span>] = <span class="built_in">max</span>(dp[s][<span class="number">1</span>], dp[s][<span class="number">0</span>]);</span><br><span class="line"> dp[s][<span class="number">0</span>] = dp[e.to][<span class="number">0</span>] + e.val;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (dp[s][<span class="number">1</span>] < dp[e.to][<span class="number">0</span>] + e.val) {</span><br><span class="line"> dp[s][<span class="number">1</span>] = dp[e.to][<span class="number">0</span>] + e.val;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs2</span><span class="params">(<span class="type">int</span> s, <span class="type">int</span> fa)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span>& e : arr[s]) {</span><br><span class="line"> <span class="keyword">if</span> (e.to == fa) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">if</span> (e.to == longest[s]) dp[e.to][<span class="number">2</span>] = <span class="built_in">max</span>(dp[s][<span class="number">1</span>], dp[s][<span class="number">2</span>]) + e.val;</span><br><span class="line"> <span class="keyword">else</span> dp[e.to][<span class="number">2</span>] = <span class="built_in">max</span>(dp[s][<span class="number">0</span>], dp[s][<span class="number">2</span>]) + e.val;</span><br><span class="line"> <span class="built_in">dfs2</span>(e.to, s);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> std::ios::<span class="built_in">sync_with_stdio</span>(<span class="literal">false</span>);</span><br><span class="line"> cin.<span class="built_in">tie</span>(<span class="literal">nullptr</span>);</span><br><span class="line"> cout.<span class="built_in">tie</span>(<span class="literal">nullptr</span>);</span><br><span class="line"> <span class="type">int</span> N;</span><br><span class="line"> <span class="keyword">while</span> (cin >> N && N) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span>& a : arr) a.<span class="built_in">clear</span>();</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="built_in">memset</span>(vis, <span class="number">0</span>, <span class="built_in">sizeof</span>(vis));</span><br><span class="line"> <span class="built_in">memset</span>(longest, <span class="number">0</span>, <span class="built_in">sizeof</span>(longest));</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">2</span>; i <= N; i++) {</span><br><span class="line"> <span class="type">int</span> u, val;</span><br><span class="line"> cin >> u >> val;</span><br><span class="line"> arr[u].<span class="built_in">emplace_back</span>(i, val);</span><br><span class="line"> arr[i].<span class="built_in">emplace_back</span>(u, val);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">dfs1</span>(<span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line"> <span class="built_in">dfs2</span>(<span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++)</span><br><span class="line"> cout << <span class="built_in">max</span>(dp[i][<span class="number">0</span>], dp[i][<span class="number">2</span>]) << endl;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Strategic-game"><a href="https://www.luogu.com.cn/problem/UVA1292">Strategic game</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">1505</span>;</span><br><span class="line"></span><br><span class="line">vector<<span class="type">int</span>> arr[maxN];</span><br><span class="line"><span class="type">int</span> dp[maxN][<span class="number">2</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> s, <span class="type">int</span> fa)</span> </span>{</span><br><span class="line"> dp[s][<span class="number">1</span>] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span> u : arr[s]) {</span><br><span class="line"> <span class="keyword">if</span> (u == fa) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="built_in">dfs</span>(u, s);</span><br><span class="line"> dp[s][<span class="number">1</span>] += <span class="built_in">min</span>(dp[u][<span class="number">1</span>], dp[u][<span class="number">0</span>]);</span><br><span class="line"> dp[s][<span class="number">0</span>] += dp[u][<span class="number">1</span>];</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">int</span> N;</span><br><span class="line"> <span class="keyword">while</span> (~<span class="built_in">scanf</span>(<span class="string">"%d"</span>, &N) && N) {</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span>& a : arr) a.<span class="built_in">clear</span>();</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < N; i++) {</span><br><span class="line"> <span class="type">int</span> number, cnt;</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%d:(%d)"</span>, &number, &cnt);</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < cnt; j++) {</span><br><span class="line"> <span class="type">int</span> u;</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &u);</span><br><span class="line"> arr[number].<span class="built_in">emplace_back</span>(u);</span><br><span class="line"> arr[u].<span class="built_in">emplace_back</span>(number);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">dfs</span>(<span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"> <span class="type">int</span> ans = <span class="built_in">min</span>(dp[<span class="number">0</span>][<span class="number">1</span>], dp[<span class="number">0</span>][<span class="number">0</span>]);</span><br><span class="line"> cout << ans << endl;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="最大子树和"><a href="https://www.luogu.com.cn/problem/P1122">最大子树和</a></h2><h3 id="思路分析-3">思路分析</h3><p>一道比较简单的树形DP题, 题意就是让你求这个树上点权和最大的一个联通部分(连通分量)。</p><p>考虑设 <code>f(i)</code> 表示以 <code>i</code> 为根的所有子树中点权和最大的一个。</p><p>对于 <code>u</code> 和 <code>u</code> 的儿子 <code>v</code>,如果 <code>f(v) >= 1</code> 则它对答案有贡献,要保留,反之则要减掉。</p><h3 id="题解代码-2">题解代码</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">16005</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> dp[maxN];</span><br><span class="line">vector<<span class="type">int</span>> arr[maxN];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> s, <span class="type">int</span> fa)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span> u : arr[s]) {</span><br><span class="line"> <span class="keyword">if</span> (u == fa) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="built_in">dfs</span>(u, s);</span><br><span class="line"> <span class="keyword">if</span> (dp[u] > <span class="number">0</span>) dp[s] += dp[u];</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> std::ios::<span class="built_in">sync_with_stdio</span>(<span class="literal">false</span>);</span><br><span class="line"> cin.<span class="built_in">tie</span>(<span class="literal">nullptr</span>);</span><br><span class="line"> cout.<span class="built_in">tie</span>(<span class="literal">nullptr</span>);</span><br><span class="line"> <span class="type">int</span> N;</span><br><span class="line"> cin >> N;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) {</span><br><span class="line"> cin >> dp[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N - <span class="number">1</span>; i++) {</span><br><span class="line"> <span class="type">int</span> u, v;</span><br><span class="line"> cin >> u >> v;</span><br><span class="line"> arr[u].<span class="built_in">emplace_back</span>(v);</span><br><span class="line"> arr[v].<span class="built_in">emplace_back</span>(u);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">dfs</span>(<span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line"> <span class="type">int</span> ans = INT_MIN;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) ans = <span class="built_in">max</span>(ans, dp[i]);</span><br><span class="line"> cout << ans << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="加分二叉树"><a href="https://www.luogu.com.cn/problem/P1040">加分二叉树</a></h2><h3 id="暴力解法">暴力解法</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">35</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">long</span> <span class="type">long</span> arrS[maxN];</span><br><span class="line"><span class="type">long</span> <span class="type">long</span> memo[maxN][maxN];</span><br><span class="line"><span class="type">int</span> memoRoot[maxN][maxN];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> left, <span class="type">int</span> right)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (memo[left][right] != <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> memo[left][right];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (left > right) {</span><br><span class="line"> <span class="keyword">return</span> memo[left][right] = <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (left == right) {</span><br><span class="line"> memoRoot[left][right] = left;</span><br><span class="line"> <span class="keyword">return</span> memo[left][right] = arrS[left];</span><br><span class="line"> }</span><br><span class="line"> <span class="type">long</span> <span class="type">long</span> ret = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = left; i <= right; i++) {</span><br><span class="line"> <span class="type">long</span> <span class="type">long</span> cur = <span class="built_in">dfs</span>(left, i - <span class="number">1</span>) * <span class="built_in">dfs</span>(i + <span class="number">1</span>, right) + arrS[i];</span><br><span class="line"> <span class="keyword">if</span> (cur > ret) {</span><br><span class="line"> memoRoot[left][right] = i;</span><br><span class="line"> ret = cur;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> memo[left][right] = ret;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">traverse</span><span class="params">(<span class="type">int</span> left, <span class="type">int</span> right)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (left > right) <span class="keyword">return</span>;</span><br><span class="line"> <span class="type">int</span> root = memoRoot[left][right];</span><br><span class="line"> cout << root << <span class="string">" "</span>;</span><br><span class="line"> <span class="built_in">traverse</span>(left, root - <span class="number">1</span>);</span><br><span class="line"> <span class="built_in">traverse</span>(root + <span class="number">1</span>, right);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">int</span> N;</span><br><span class="line"> cin >> N;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) cin >> arrS[i];</span><br><span class="line"> <span class="type">long</span> <span class="type">long</span> ans = <span class="built_in">dfs</span>(<span class="number">1</span>, N);</span><br><span class="line"> cout << ans << endl;</span><br><span class="line"> <span class="built_in">traverse</span>(<span class="number">1</span>, N);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1>树上背包</h1><p>树上的背包问题,简单来说就是背包问题与树形 DP 的结合。</p><h2 id="选课"><a href="https://www.luogu.com.cn/problem/P2014">选课</a></h2><p>现在有 n 门课程,第 i 门课程的学分为 a_i,每门课程有零门或一门先修课,有先修课的课程需要先学完其先修课,才能学习该课程。</p><p>一位学生要学习 m 门课程,求其能获得的最多学分数。</p><h3 id="思路分析-4">思路分析</h3><p>每门课最多只有一门先修课的特点,与有根树中一个点最多只有一个父亲结点的特点类似。</p><p>因此可以想到根据这一性质建树,从而所有课程组成了一个森林的结构。为了方便起见,我们可以新增一门 0 学分的课程(设这个课程的编号为 0),作为所有无先修课课程的先修课,这样我们就将森林变成了一棵以 0 号课程为根的树。</p><p>我们设 <code>f(u,i,j)</code> 表示以 u 号点为根的子树中,已经遍历了 u 号点的前 i 棵子树,选了 j 门课程的最大学分。</p><p>转移的过程结合了树形 DP 和 背包 DP 的特点,我们枚举 u 点的每个子结点 v,同时枚举以 v 为根的子树选了几门课程,将子树的结果合并到 u 上。</p><p>记点 x 的儿子个数为 sX,以 x 为根的子树大小为 <code>sizeX</code>,可以写出下面的状态转移方程:</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>u</mi><mo separator="true">,</mo><mi>i</mi><mo separator="true">,</mo><mi>j</mi><mo stretchy="false">)</mo><mo>=</mo><munder><mrow><mi>max</mi><mo></mo></mrow><mrow><mi>v</mi><mo separator="true">,</mo><mi>k</mi><mo>≤</mo><mi>j</mi><mo separator="true">,</mo><mi>k</mi><mo>≤</mo><mi>s</mi><mi>i</mi><mi>z</mi><mi>e</mi><mi>X</mi></mrow></munder><mi>f</mi><mo stretchy="false">(</mo><mi>u</mi><mo separator="true">,</mo><mi>i</mi><mo>−</mo><mn>1</mn><mo separator="true">,</mo><mi>j</mi><mo>−</mo><mi>k</mi><mo stretchy="false">)</mo><mo>+</mo><mi>f</mi><mo stretchy="false">(</mo><mi>v</mi><mo separator="true">,</mo><mi>s</mi><mi>V</mi><mo separator="true">,</mo><mi>k</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">f(u,i,j)=\max_{v,k \leq j,k \leq sizeX } f(u,i-1,j-k)+f(v,sV,k) </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mopen">(</span><span class="mord mathnormal">u</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">i</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05724em;">j</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.6382em;vertical-align:-0.8882em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.4306em;"><span style="top:-2.3479em;margin-left:0em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.03588em;">v</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span><span class="mrel mtight">≤</span><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span><span class="mrel mtight">≤</span><span class="mord mathnormal mtight">s</span><span class="mord mathnormal mtight">i</span><span class="mord mathnormal mtight">ze</span><span class="mord mathnormal mtight" style="margin-right:0.07847em;">X</span></span></span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span><span class="mop">max</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.8882em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mopen">(</span><span class="mord mathnormal">u</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">i</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.854em;vertical-align:-0.1944em;"></span><span class="mord">1</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05724em;">j</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">s</span><span class="mord mathnormal" style="margin-right:0.22222em;">V</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mclose">)</span></span></span></span></span></p><p>注意上面状态转移方程中的几个限制条件,这些限制条件确保了一些无意义的状态不会被访问到。</p><p>f 的第二维可以很轻松地用滚动数组的方式省略掉,注意这时需要倒序枚举 j 的值。</p><h3 id="题解代码-3">题解代码</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">305</span>;</span><br><span class="line"></span><br><span class="line">vector<<span class="type">int</span>> arr[maxN];</span><br><span class="line"><span class="type">int</span> dp[maxN][maxN];</span><br><span class="line"><span class="type">int</span> arrW[maxN];</span><br><span class="line"><span class="type">int</span> arrV[maxN];</span><br><span class="line"><span class="type">int</span> N, M;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> s, <span class="type">int</span> fa)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = M; i >= arrV[s]; i--) dp[s][i] = arrW[s];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span> x : arr[s]) {</span><br><span class="line"> <span class="keyword">if</span> (x == fa) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="built_in">dfs</span>(x, s);</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = M; i >= arrV[s]; i--)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; i - j >= arrV[s]; j++)</span><br><span class="line"> dp[s][i] = <span class="built_in">max</span>(dp[s][i], dp[s][i - j] + dp[x][j]);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> std::ios::<span class="built_in">sync_with_stdio</span>(<span class="literal">false</span>);</span><br><span class="line"> cin.<span class="built_in">tie</span>(<span class="literal">nullptr</span>);</span><br><span class="line"> cout.<span class="built_in">tie</span>(<span class="literal">nullptr</span>);</span><br><span class="line"> cin >> N >> M;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) {</span><br><span class="line"> <span class="type">int</span> k;</span><br><span class="line"> cin >> k >> arrW[i];</span><br><span class="line"> arrV[i] = <span class="number">1</span>;</span><br><span class="line"> arr[k].<span class="built_in">emplace_back</span>(i);</span><br><span class="line"> }</span><br><span class="line"> arrV[<span class="number">0</span>] = <span class="number">0</span>, arrW[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="built_in">dfs</span>(<span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"> cout << dp[<span class="number">0</span>][M] << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="二叉苹果树"><a href="https://www.luogu.com.cn/problem/P2015">二叉苹果树</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">105</span>;</span><br><span class="line"></span><br><span class="line">vector<Edge> arr[maxN];</span><br><span class="line"><span class="type">int</span> dim[maxN];</span><br><span class="line"><span class="type">int</span> dp[maxN][maxN];</span><br><span class="line"><span class="type">int</span> siz[maxN];</span><br><span class="line"><span class="type">int</span> N, Q;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> s, <span class="type">int</span> fa)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span>& e : arr[s]) {</span><br><span class="line"> <span class="keyword">if</span> (e.to == fa) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="built_in">dfs</span>(e.to, s);</span><br><span class="line"> siz[s] += <span class="number">1</span> + siz[e.to];</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="built_in">min</span>(siz[s], Q); i >= <span class="number">0</span>; i--) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="built_in">min</span>(i - <span class="number">1</span>, siz[e.to]); j >= <span class="number">0</span>; j--) {</span><br><span class="line"> dp[s][i] = <span class="built_in">max</span>(dp[s][i], dp[s][i - j - <span class="number">1</span>] + dp[e.to][j] + e.val);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="built_in">memset</span>(siz, <span class="number">0</span>, <span class="built_in">sizeof</span>(siz));</span><br><span class="line"> <span class="built_in">memset</span>(dim, <span class="number">0</span>, <span class="built_in">sizeof</span>(dim));</span><br><span class="line"> cin >> N >> Q;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N - <span class="number">1</span>; i++) {</span><br><span class="line"> <span class="type">int</span> u, v, w;</span><br><span class="line"> cin >> u >> v >> w;</span><br><span class="line"> arr[u].<span class="built_in">emplace_back</span>(v, w);</span><br><span class="line"> arr[v].<span class="built_in">emplace_back</span>(u, w);</span><br><span class="line"> dim[u]++, dim[v]++;</span><br><span class="line"> }</span><br><span class="line"> <span class="type">int</span> root = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++)</span><br><span class="line"> <span class="keyword">if</span> (arr[i].<span class="built_in">size</span>() == <span class="number">2</span>) {</span><br><span class="line"> root = i;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">dfs</span>(root, root);</span><br><span class="line"> cout << dp[root][Q] << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1>换根 DP</h1><p>树形 DP 中的换根 DP 问题又被称为二次扫描,通常不会指定根结点,并且根结点的变化会对一些值,例如子结点深度和、点权和等产生影响。</p><p>通常需要两次 DFS,第一次 DFS 预处理诸如深度,点权和之类的信息,在第二次 DFS 开始运行换根动态规划。</p><h2 id="STA-Station"><a href="https://www.luogu.com.cn/problem/P3478">STA-Station</a></h2><p>给定一个 n 个点的树,请求出一个结点,使得以这个结点为根时,所有结点的深度之和最大。</p><h3 id="思路分析-5">思路分析</h3><p>不妨令 <code>u</code> 为当前结点,<code>v</code> 为当前结点的子结点。首先需要用 <code>s_i</code> 来表示以 <code>i</code> 为根的子树中的结点个数,并且有 <code>s_u=1+sum{s_v}</code>。显然需要一次 DFS 来计算所有的 <code>s_i</code>,这次的 DFS 就是预处理,我们得到了以某个结点为根时其子树中的结点总数。</p><p>考虑状态转移,这里就是体现"换根"的地方了。令 <code>f_u</code> 为以 <code>u</code> 为根时,所有结点的深度之和。</p><p><code>f_v <-- f_u</code> 可以体现换根,即以 <code>u</code> 为根转移到以 <code>v</code> 为根。显然在换根的转移过程中,以 <code>v</code> 为根或以 <code>u</code> 为根会导致其子树中的结点的深度产生改变。具体表现为:</p><ol><li>所有在 v 的子树上的结点深度都减少了一,那么总深度和就减少了 <code>s_v</code>;</li><li>所有不在 v 的子树上的结点深度都增加了一,那么总深度和就增加了 <code>n-s_v</code>;</li></ol><p>根据这两个条件就可以推出状态转移方程<code> f_v = f_u - s_v + n - s_v = f_u + n - 2 * s_v</code>。</p><p>于是在第二次 DFS 遍历整棵树并状态转移 <code>f_v=f_u + n - 2 * s_v</code>,那么就能求出以每个结点为根时的深度和了。最后只需要遍历一次所有根结点深度和就可以求出答案。</p><h3 id="代码实现-2">代码实现</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">1e6</span> + <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line">vector<<span class="type">int</span>> arr[maxN];</span><br><span class="line"><span class="type">int</span> siz[maxN];</span><br><span class="line"><span class="type">int</span> dep[maxN];</span><br><span class="line"><span class="type">long</span> <span class="type">long</span> dp[maxN];</span><br><span class="line"><span class="type">int</span> N;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs1</span><span class="params">(<span class="type">int</span> s, <span class="type">int</span> pre)</span> </span>{</span><br><span class="line"> siz[s] = <span class="number">1</span>;</span><br><span class="line"> dep[s] = dep[pre] + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span> u : arr[s]) {</span><br><span class="line"> <span class="keyword">if</span> (u == pre) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="built_in">dfs1</span>(u, s);</span><br><span class="line"> siz[s] += siz[u];</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs2</span><span class="params">(<span class="type">int</span> s, <span class="type">int</span> pre)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span> u : arr[s]) {</span><br><span class="line"> <span class="keyword">if</span> (u == pre) <span class="keyword">continue</span>;</span><br><span class="line"> dp[u] = dp[s] + N - <span class="number">2</span> * siz[u];</span><br><span class="line"> <span class="built_in">dfs2</span>(u, s);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> std::ios::<span class="built_in">sync_with_stdio</span>(<span class="literal">false</span>);</span><br><span class="line"> cin.<span class="built_in">tie</span>(<span class="literal">nullptr</span>);</span><br><span class="line"> cout.<span class="built_in">tie</span>(<span class="literal">nullptr</span>);</span><br><span class="line"> cin >> N;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i < N; i++) {</span><br><span class="line"> <span class="type">int</span> u, v;</span><br><span class="line"> cin >> u >> v;</span><br><span class="line"> arr[u].<span class="built_in">emplace_back</span>(v);</span><br><span class="line"> arr[v].<span class="built_in">emplace_back</span>(u);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">dfs1</span>(<span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line"> dp[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line"> dep[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) dp[<span class="number">1</span>] += dep[i];</span><br><span class="line"> <span class="built_in">dfs2</span>(<span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line"> <span class="type">long</span> <span class="type">long</span> ans = <span class="number">-1</span>;</span><br><span class="line"> <span class="type">int</span> index = <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) <span class="keyword">if</span> (dp[i] > ans) ans = dp[i], index = i;</span><br><span class="line"> cout << index << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Accumulation-Degree"><a href="https://www.acwing.com/problem/content/289/">Accumulation Degree</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> inf = <span class="number">0x3f3f3f3f</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">2e5</span> + <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Edge</span> {</span><br><span class="line"> <span class="type">int</span> to;</span><br><span class="line"> <span class="type">int</span> val;</span><br><span class="line"> <span class="built_in">Edge</span>(<span class="type">int</span> to, <span class="type">int</span> val) : <span class="built_in">to</span>(to), <span class="built_in">val</span>(val) {}</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">vector<Edge> arr[maxN];</span><br><span class="line"><span class="type">int</span> dp[maxN][<span class="number">2</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs1</span><span class="params">(<span class="type">int</span> s, <span class="type">int</span> fa)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span>& e : arr[s]) {</span><br><span class="line"> <span class="keyword">if</span> (e.to == fa) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="built_in">dfs1</span>(e.to, s);</span><br><span class="line"> <span class="keyword">if</span> (arr[e.to].<span class="built_in">size</span>() == <span class="number">1</span>) dp[s][<span class="number">0</span>] += e.val;</span><br><span class="line"> <span class="keyword">else</span> dp[s][<span class="number">0</span>] += <span class="built_in">min</span>(e.val, dp[e.to][<span class="number">0</span>]);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs2</span><span class="params">(<span class="type">int</span> s, <span class="type">int</span> fa)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span>& e : arr[s]) {</span><br><span class="line"> <span class="keyword">if</span> (e.to == fa) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">if</span> (arr[s].<span class="built_in">size</span>() == <span class="number">1</span>) dp[e.to][<span class="number">1</span>] = e.val;</span><br><span class="line"> <span class="keyword">else</span> dp[e.to][<span class="number">1</span>] = <span class="built_in">min</span>(e.val, dp[s][<span class="number">1</span>] + dp[s][<span class="number">0</span>] - <span class="built_in">min</span>(e.val, dp[e.to][<span class="number">0</span>]));</span><br><span class="line"> <span class="built_in">dfs2</span>(e.to, s);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">int</span> T;</span><br><span class="line"> cin >> T;</span><br><span class="line"> <span class="keyword">while</span> (T--) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span>& a : arr) a.<span class="built_in">clear</span>();</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="type">int</span> N;</span><br><span class="line"> cin >> N;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N - <span class="number">1</span>; i++) {</span><br><span class="line"> <span class="type">int</span> x, y, z;</span><br><span class="line"> cin >> x >> y >> z;</span><br><span class="line"> arr[x].<span class="built_in">emplace_back</span>(y, z);</span><br><span class="line"> arr[y].<span class="built_in">emplace_back</span>(x, z);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">dfs1</span>(<span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line"> <span class="built_in">dfs2</span>(<span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line"> <span class="type">int</span> maxAns = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) <span class="keyword">if</span> (dp[i][<span class="number">0</span>] + dp[i][<span class="number">1</span>] > maxAns) maxAns = dp[i][<span class="number">0</span>] + dp[i][<span class="number">1</span>];</span><br><span class="line"> cout << maxAns << endl;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Great-Cow-Gathering-G"><a href="https://www.luogu.com.cn/problem/P2986">Great Cow Gathering G</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">Edge</span> {</span><br><span class="line"> <span class="type">int</span> to;</span><br><span class="line"> <span class="type">int</span> len;</span><br><span class="line"> <span class="built_in">Edge</span>(<span class="type">int</span> to, <span class="type">int</span> len) : <span class="built_in">to</span>(to), <span class="built_in">len</span>(len) {}</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">1e5</span> + <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">long</span> <span class="type">long</span> siz[maxN];</span><br><span class="line">vector<Edge> arr[maxN];</span><br><span class="line"><span class="type">long</span> <span class="type">long</span> dp[maxN];</span><br><span class="line"><span class="type">long</span> <span class="type">long</span> sumSize;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs1</span><span class="params">(<span class="type">int</span> s, <span class="type">int</span> fa)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span>& e : arr[s]) {</span><br><span class="line"> <span class="keyword">if</span> (e.to == fa) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="built_in">dfs1</span>(e.to, s);</span><br><span class="line"> siz[s] += siz[e.to];</span><br><span class="line"> dp[s] += dp[e.to] + siz[e.to] * e.len;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs2</span><span class="params">(<span class="type">int</span> s, <span class="type">int</span> fa)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span>& e : arr[s]) {</span><br><span class="line"> <span class="keyword">if</span> (e.to == fa) <span class="keyword">continue</span>;</span><br><span class="line"> dp[e.to] = dp[s] - siz[e.to] * e.len + (sumSize - siz[e.to]) * e.len;</span><br><span class="line"> <span class="built_in">dfs2</span>(e.to, s);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> std::ios::<span class="built_in">sync_with_stdio</span>(<span class="literal">false</span>);</span><br><span class="line"> cin.<span class="built_in">tie</span>(<span class="literal">nullptr</span>);</span><br><span class="line"> cout.<span class="built_in">tie</span>(<span class="literal">nullptr</span>);</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="built_in">memset</span>(siz, <span class="number">0</span>, <span class="built_in">sizeof</span>(siz));</span><br><span class="line"> <span class="type">int</span> N;</span><br><span class="line"> cin >> N;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) cin >> siz[i], sumSize += siz[i];</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N - <span class="number">1</span>; i++) {</span><br><span class="line"> <span class="type">int</span> a, b, l;</span><br><span class="line"> cin >> a >> b >> l;</span><br><span class="line"> arr[a].<span class="built_in">emplace_back</span>(b, l);</span><br><span class="line"> arr[b].<span class="built_in">emplace_back</span>(a, l);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">dfs1</span>(<span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line"> <span class="built_in">dfs2</span>(<span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line"> <span class="type">long</span> <span class="type">long</span> ans = LLONG_MAX;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) ans = <span class="built_in">min</span>(ans, dp[i]);</span><br><span class="line"> cout << ans << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Centroids"><a href="https://codeforces.com/problemset/problem/708/C">Centroids</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure><h1>高难度</h1><h2 id="潜入行动"><a href="https://www.luogu.com.cn/problem/P4516">潜入行动</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">long</span> <span class="type">long</span> p = <span class="number">1e9</span> + <span class="number">7</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">1e5</span> + <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> N, K;</span><br><span class="line">vector<<span class="type">int</span>> arr[maxN];</span><br><span class="line"><span class="type">int</span> dp[maxN][<span class="number">105</span>][<span class="number">2</span>][<span class="number">2</span>];</span><br><span class="line"><span class="type">int</span> siz[maxN];</span><br><span class="line"><span class="type">long</span> <span class="type">long</span> temp[<span class="number">105</span>][<span class="number">2</span>][<span class="number">2</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">void</span> <span class="title">add</span><span class="params">(<span class="type">int</span>& x, <span class="type">long</span> <span class="type">long</span> y)</span> </span>{</span><br><span class="line"> y %= p;</span><br><span class="line"> x += y;</span><br><span class="line"> x %= p;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> s, <span class="type">int</span> fa)</span> </span>{</span><br><span class="line"> siz[s] = dp[s][<span class="number">0</span>][<span class="number">0</span>][<span class="number">0</span>] = dp[s][<span class="number">1</span>][<span class="number">1</span>][<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span> u : arr[s]) {</span><br><span class="line"> <span class="keyword">if</span> (u == fa) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="built_in">dfs</span>(u, s);</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i <= <span class="built_in">min</span>(K, siz[s]); i++) {</span><br><span class="line"> temp[i][<span class="number">0</span>][<span class="number">0</span>] = dp[s][i][<span class="number">0</span>][<span class="number">0</span>], dp[s][i][<span class="number">0</span>][<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> temp[i][<span class="number">0</span>][<span class="number">1</span>] = dp[s][i][<span class="number">0</span>][<span class="number">1</span>], dp[s][i][<span class="number">0</span>][<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line"> temp[i][<span class="number">1</span>][<span class="number">0</span>] = dp[s][i][<span class="number">1</span>][<span class="number">0</span>], dp[s][i][<span class="number">1</span>][<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> temp[i][<span class="number">1</span>][<span class="number">1</span>] = dp[s][i][<span class="number">1</span>][<span class="number">1</span>], dp[s][i][<span class="number">1</span>][<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i <= <span class="built_in">min</span>(siz[s], K); i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j <= <span class="built_in">min</span>(siz[u], K - i); j++) {</span><br><span class="line"> <span class="built_in">add</span>(dp[s][i + j][<span class="number">0</span>][<span class="number">0</span>], temp[i][<span class="number">0</span>][<span class="number">0</span>] * dp[u][j][<span class="number">0</span>][<span class="number">1</span>]);</span><br><span class="line"> <span class="built_in">add</span>(dp[s][i + j][<span class="number">0</span>][<span class="number">1</span>], temp[i][<span class="number">0</span>][<span class="number">0</span>] * (dp[u][j][<span class="number">1</span>][<span class="number">1</span>]));</span><br><span class="line"> <span class="built_in">add</span>(dp[s][i + j][<span class="number">0</span>][<span class="number">1</span>], temp[i][<span class="number">0</span>][<span class="number">1</span>] * (<span class="number">0LL</span>+dp[u][j][<span class="number">1</span>][<span class="number">1</span>] + dp[u][j][<span class="number">0</span>][<span class="number">1</span>]));</span><br><span class="line"> <span class="built_in">add</span>(dp[s][i + j][<span class="number">1</span>][<span class="number">0</span>], temp[i][<span class="number">1</span>][<span class="number">0</span>] * (<span class="number">0LL</span>+dp[u][j][<span class="number">0</span>][<span class="number">1</span>] + dp[u][j][<span class="number">0</span>][<span class="number">0</span>]));</span><br><span class="line"> <span class="built_in">add</span>(dp[s][i + j][<span class="number">1</span>][<span class="number">1</span>], temp[i][<span class="number">1</span>][<span class="number">0</span>] * (<span class="number">0LL</span>+dp[u][j][<span class="number">1</span>][<span class="number">1</span>] + dp[u][j][<span class="number">1</span>][<span class="number">0</span>]));</span><br><span class="line"> <span class="built_in">add</span>(dp[s][i + j][<span class="number">1</span>][<span class="number">1</span>],temp[i][<span class="number">1</span>][<span class="number">1</span>] *</span><br><span class="line"> (<span class="number">0LL</span>+dp[u][j][<span class="number">0</span>][<span class="number">0</span>] + dp[u][j][<span class="number">0</span>][<span class="number">1</span>] + dp[u][j][<span class="number">1</span>][<span class="number">0</span>] + dp[u][j][<span class="number">1</span>][<span class="number">1</span>]));</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> siz[s] += siz[u];</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> cin >> N >> K;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N - <span class="number">1</span>; i++) {</span><br><span class="line"> <span class="type">int</span> u, v;</span><br><span class="line"> cin >> u >> v;</span><br><span class="line"> arr[u].<span class="built_in">emplace_back</span>(v);</span><br><span class="line"> arr[v].<span class="built_in">emplace_back</span>(u);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="built_in">dfs</span>(<span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line"> cout << (dp[<span class="number">1</span>][K][<span class="number">0</span>][<span class="number">1</span>] + dp[<span class="number">1</span>][K][<span class="number">1</span>][<span class="number">1</span>]) << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="苹果树"><a href="https://www.luogu.com.cn/problem/P3780">苹果树</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure><h2 id="FAR-FarmCraft"><a href="https://www.luogu.com.cn/problem/P3574">FAR-FarmCraft</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h1>概括</h1>
<p>树形 DP,即在树上进行的 DP。由于树固有的递归性质,树形 DP 一般都是递归进行的。</p>
<h1>基础</h1>
<h2 id="没有上司的舞会"><a href="https://www.luogu.com.cn/problem/P1352</summary>
<category term="算法" scheme="https://creating001.github.io/categories/%E7%AE%97%E6%B3%95/"/>
<category term="动态规划" scheme="https://creating001.github.io/categories/%E7%AE%97%E6%B3%95/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="动态规划" scheme="https://creating001.github.io/tags/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="树形dp" scheme="https://creating001.github.io/tags/%E6%A0%91%E5%BD%A2dp/"/>
</entry>
<entry>
<title>动态规划之区间dp</title>
<link href="https://creating001.github.io/2023/05/19/dp-3-interval/"/>
<id>https://creating001.github.io/2023/05/19/dp-3-interval/</id>
<published>2023-05-18T17:22:27.000Z</published>
<updated>2023-06-02T11:42:02.749Z</updated>
<content type="html"><![CDATA[<h1>定义</h1><p>区间类动态规划是线性动态规划的扩展,它在分阶段地划分问题时,与阶段中元素出现的顺序和由前一阶段的哪些元素合并而来有很大的关系。</p><p>令状态 <code>f(i,j)</code> 表示将下标位置 <code>i</code> 到 <code>j</code> 的所有元素合并能获得的价值的最大值,那么</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>i</mi><mo separator="true">,</mo><mi>j</mi><mo stretchy="false">)</mo><mo>=</mo><mi>max</mi><mo></mo><mo stretchy="false">{</mo><mi>f</mi><mo stretchy="false">(</mo><mi>i</mi><mo separator="true">,</mo><mi>k</mi><mo stretchy="false">)</mo><mo>+</mo><mi>f</mi><mo stretchy="false">(</mo><mi>k</mi><mo>+</mo><mn>1</mn><mo separator="true">,</mo><mi>j</mi><mo stretchy="false">)</mo><mo>+</mo><mi>c</mi><mi>o</mi><mi>s</mi><mi>t</mi><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">f(i,j)=\max\{f(i,k)+f(k+1,j)+cost\} </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mopen">(</span><span class="mord mathnormal">i</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05724em;">j</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mop">max</span><span class="mopen">{</span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mopen">(</span><span class="mord mathnormal">i</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">1</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05724em;">j</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">cos</span><span class="mord mathnormal">t</span><span class="mclose">}</span></span></span></span></span></p><p><code>cost</code> 为将这两组元素合并起来的代价。</p><h1>性质</h1><p>区间 DP 有以下特点:</p><ol><li>合并:即将两个或多个部分进行整合,当然也可以反过来;</li><li>特征:能将问题分解为能两两合并的形式;</li><li>求解:对整个问题设最优值,枚举合并点,将问题分解为左右两个部分,最后合并两个部分的最优值得到原问题的最优值。</li></ol><h1>典型例题</h1><h2 id="石子合并1"><a href="https://www.luogu.com.cn/problem/P1775">石子合并1</a></h2><p>在一个操场上摆放着一排 <code>N</code> 堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的 2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。</p><p>试设计一个算法,计算出将 <code>N</code> 堆石子合并成一堆的最小得分。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">305</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> arr[maxN];</span><br><span class="line"><span class="type">int</span> prefix[maxN];</span><br><span class="line"><span class="type">int</span> dp[maxN][maxN];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> std::ios::<span class="built_in">sync_with_stdio</span>(<span class="literal">false</span>);</span><br><span class="line"> <span class="type">int</span> N;</span><br><span class="line"> cin >> N;</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0x3f</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> prefix[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) {</span><br><span class="line"> cin >> arr[i];</span><br><span class="line"> prefix[i] = prefix[i - <span class="number">1</span>] + arr[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) dp[i][i] = <span class="number">0</span>, dp[i][i + <span class="number">1</span>] = arr[i] + arr[i + <span class="number">1</span>];</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">2</span>; k < N; k++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i + k <= N; i++) {</span><br><span class="line"> <span class="type">int</span> j = i + k;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> t = i; t < j; t++) {</span><br><span class="line"> dp[i][j] = <span class="built_in">min</span>(dp[i][j], dp[i][t] + dp[t + <span class="number">1</span>][j] + prefix[j] - prefix[i - <span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << dp[<span class="number">1</span>][N] << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="石子合并2"><a href="https://www.luogu.com.cn/problem/P1880">石子合并2</a></h2><p>在一个圆形操场的四周摆放 <code>N</code> 堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的 2 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。</p><p>试设计出一个算法,计算出将 <code>N</code> 堆石子合并成 1 堆的最小得分和最大得分。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">205</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> arr[maxN];</span><br><span class="line"><span class="type">int</span> prefix[maxN];</span><br><span class="line"><span class="type">int</span> dpMax[maxN][maxN];</span><br><span class="line"><span class="type">int</span> dpMin[maxN][maxN];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> std::ios::<span class="built_in">sync_with_stdio</span>(<span class="literal">false</span>);</span><br><span class="line"> <span class="type">int</span> N;</span><br><span class="line"> cin >> N;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) {</span><br><span class="line"> cin >> arr[i];</span><br><span class="line"> arr[i + N] = arr[i];</span><br><span class="line"> }</span><br><span class="line"> prefix[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= <span class="number">2</span> * N; i++) {</span><br><span class="line"> prefix[i] += prefix[i - <span class="number">1</span>] + arr[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">memset</span>(dpMax, <span class="number">0</span>, <span class="built_in">sizeof</span>(dpMax));</span><br><span class="line"> <span class="built_in">memset</span>(dpMin, <span class="number">0x3f</span>, <span class="built_in">sizeof</span>(dpMin));</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">2</span> * N; i++) dpMax[i][i] = <span class="number">0</span>, dpMin[i][i] = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">1</span>; k < N; k++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i + k <= <span class="number">2</span> * N; i++) {</span><br><span class="line"> <span class="type">int</span> j = i + k;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> t = i; t < j; t++) {</span><br><span class="line"> dpMax[i][j] = <span class="built_in">max</span>(dpMax[i][j], dpMax[i][t] + dpMax[t + <span class="number">1</span>][j] + prefix[j] - prefix[i - <span class="number">1</span>]);</span><br><span class="line"> dpMin[i][j] = <span class="built_in">min</span>(dpMin[i][j], dpMin[i][t] + dpMin[t + <span class="number">1</span>][j] + prefix[j] - prefix[i - <span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="type">int</span> ansMax = <span class="number">0</span>;</span><br><span class="line"> <span class="type">int</span> ansMin = INT_MAX;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) {</span><br><span class="line"> ansMax = <span class="built_in">max</span>(ansMax, dpMax[i][i + N - <span class="number">1</span>]);</span><br><span class="line"> ansMin = <span class="built_in">min</span>(ansMin, dpMin[i][i + N - <span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line"> cout << ansMin << endl;</span><br><span class="line"> cout << ansMax << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="石子合并3"><a href="https://leetcode.cn/problems/minimum-cost-to-merge-stones/">石子合并3</a></h2><p>有 n 堆石头排成一排,第 i 堆中有 <code>stones[i]</code> 块石头。每次 移动 需要将 连续的 k 堆石头合并为一堆,而这次移动的成本为这 k 堆中石头的总数。</p><p>返回把所有石头合并成一堆的最低成本。如果无法合并成一堆,返回 -1 。</p><h3 id="思路分析">思路分析</h3><p>n 大于 1 时若想将 n 堆石子合并为 1 堆,我们首先准备好不同的 k 堆,因此可以用 <code>d[l][r][t]</code> 描述这个状态,表示将 <code>[l,r]</code> 合并为 <code>t (1≤t≤k)</code> 堆的最低成本。与 <code>k=2</code> 时的思考方式一致,我们考虑一个分界点 <code>p (l≤p<r)</code>,令 <code>[l,p]</code> 合并为 1 堆,再令 <code>[p+1,r]</code> 合并为 <code>t−1</code> 堆,这样就可以将问题拆分为两个子问题进行求解。</p><h3 id="区间dp">区间dp</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> {</span><br><span class="line"> <span class="type">static</span> <span class="keyword">constexpr</span> <span class="type">int</span> inf = <span class="number">0x3f3f3f3f</span>;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">int</span> <span class="title">mergeStones</span><span class="params">(vector<<span class="type">int</span>>& stones, <span class="type">int</span> k)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> ((stones.<span class="built_in">size</span>() - <span class="number">1</span>) % (k - <span class="number">1</span>) != <span class="number">0</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> <span class="function">vector <span class="title">dp</span><span class="params">(stones.size(), vector(stones.size(), vector(k + <span class="number">1</span>, inf)))</span></span>;</span><br><span class="line"> <span class="function">vector <span class="title">sum</span><span class="params">(stones.size() + <span class="number">1</span>, <span class="number">0</span>)</span></span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < stones.<span class="built_in">size</span>(); i++) {</span><br><span class="line"> sum[i + <span class="number">1</span>] = sum[i] + stones[i];</span><br><span class="line"> dp[i][i][<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> len = <span class="number">2</span>; len <= stones.<span class="built_in">size</span>(); len++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i + len - <span class="number">1</span> < stones.<span class="built_in">size</span>(); i++) {</span><br><span class="line"> <span class="type">int</span> j = i + len - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> t = <span class="number">2</span>; t <= k; t++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> p = i; p < j; p += k - <span class="number">1</span>) {</span><br><span class="line"> dp[i][j][t] = <span class="built_in">min</span>(dp[i][j][t],</span><br><span class="line"> dp[i][p][<span class="number">1</span>] + dp[p + <span class="number">1</span>][j][t - <span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> dp[i][j][<span class="number">1</span>] = <span class="built_in">min</span>(dp[i][j][<span class="number">1</span>], dp[i][j][k] + sum[j + <span class="number">1</span>] - sum[i]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> dp[<span class="number">0</span>][stones.<span class="built_in">size</span>() - <span class="number">1</span>][<span class="number">1</span>];</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h3 id="状态优化">状态优化</h3><p>在方法一中,我们用 <code>d[l][r][t]</code> 表示将区间 <code>[l,r]</code> 的石头堆合并为 t 堆的最小成本,这里 t 的范围是 <code>[1,k]</code>。事实上,对于一个固定的区间 <code>[l,r]</code>,最终合并到小于 k 堆时的堆数是固定的。</p><p>我们每次合并都会减小 <code>k−1</code> 堆,初始时 <code>[l,r]</code> 的堆数是 <code>r−l+1</code>,合并到不能合并时的堆数为 <code>(r−l)mod (k−1)+1</code>。所以我们可以直接用 <code>d[l][r]</code> 表示将区间 <code>[l,r]</code> 合并到不能为止时的最小成本。它本质上是通过忽略方法一中一定无解的状态,加快求解。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> {</span><br><span class="line"> <span class="type">static</span> <span class="keyword">constexpr</span> <span class="type">int</span> inf = <span class="number">0x3f3f3f3f</span>;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">int</span> <span class="title">mergeStones</span><span class="params">(vector<<span class="type">int</span>>& stones, <span class="type">int</span> k)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> ((stones.<span class="built_in">size</span>() - <span class="number">1</span>) % (k - <span class="number">1</span>) != <span class="number">0</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> <span class="function">vector <span class="title">dp</span><span class="params">(stones.size(), vector(stones.size(), inf))</span></span>;</span><br><span class="line"> <span class="function">vector <span class="title">sum</span><span class="params">(stones.size() + <span class="number">1</span>, <span class="number">0</span>)</span></span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < stones.<span class="built_in">size</span>(); i++) {</span><br><span class="line"> sum[i + <span class="number">1</span>] = sum[i] + stones[i];</span><br><span class="line"> dp[i][i] = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> len = <span class="number">2</span>; len <= stones.<span class="built_in">size</span>(); len++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i + len - <span class="number">1</span> < stones.<span class="built_in">size</span>(); i++) {</span><br><span class="line"> <span class="type">int</span> j = i + len - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> p = i; p < j; p += k - <span class="number">1</span>) {</span><br><span class="line"> dp[i][j] = <span class="built_in">min</span>(dp[i][j], dp[i][p] + dp[p + <span class="number">1</span>][j]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ((len - <span class="number">1</span>) % (k - <span class="number">1</span>) == <span class="number">0</span>) {</span><br><span class="line"> dp[i][j] += sum[j + <span class="number">1</span>] - sum[i];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> dp[<span class="number">0</span>][stones.<span class="built_in">size</span>() - <span class="number">1</span>];</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h2 id="能量环"><a href="https://www.luogu.com.cn/problem/P1063">能量环</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">205</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> arr[maxN];</span><br><span class="line"><span class="type">int</span> dp[maxN][maxN];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">int</span> N;</span><br><span class="line"> cin >> N;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) {</span><br><span class="line"> cin >> arr[i];</span><br><span class="line"> arr[i + N] = arr[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> len = <span class="number">2</span>; len <= N; len++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i + len - <span class="number">1</span> <= <span class="number">2</span> * N; i++) {</span><br><span class="line"> <span class="type">int</span> j = i + len - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> t = i; t < j; t++) {</span><br><span class="line"> dp[i][j] = <span class="built_in">max</span>(dp[i][j], dp[i][t] + dp[t + <span class="number">1</span>][j] + arr[i] * arr[t + <span class="number">1</span>] * arr[j + <span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="type">int</span> ans = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) {</span><br><span class="line"> ans = <span class="built_in">max</span>(ans, dp[i][i + N - <span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line"> cout << ans << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="合唱队"><a href="https://www.luogu.com.cn/problem/P3205">合唱队</a></h2><p>看了题目发现要用区间 <code>dp</code>,为什么?</p><p>我们发现区间 <code>dp</code> 有一个性质——大区间包含小区间,这道题就符合这样的一个性质。</p><p>所以我们要用区间 <code>dp</code> 来解决这道题。 如何设计状态 那么我们要怎么设计状态,我们想,每给人进入队伍里,只有 2 种可能: 从左边加入; 从右边进入。 所以我们的状态是有3个数: <code>F(i,j,0)</code> 表示的是第 <code>i</code> 人从左边进来的方案数; <code>F(i,j,1)</code> 表示的是第 <code>j</code> 人从右边进来的方案数。</p><p>那么状态转移方程就出来了:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (a[i] < a[i + <span class="number">1</span>]) f[i][j][<span class="number">0</span>] += f[i + <span class="number">1</span>][j][<span class="number">0</span>];</span><br><span class="line"><span class="keyword">if</span> (a[i] < a[j]) f[i][j][<span class="number">0</span>] += f[i + <span class="number">1</span>][j][<span class="number">1</span>];</span><br><span class="line"><span class="keyword">if</span> (a[j] > a[i]) f[i][j][<span class="number">1</span>] += f[i][j - <span class="number">1</span>][<span class="number">0</span>];</span><br><span class="line"><span class="keyword">if</span> (a[j] > a[j - <span class="number">1</span>]) f[i][j][<span class="number">1</span>] += f[i][j - <span class="number">1</span>][<span class="number">1</span>];</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">1005</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> p = <span class="number">19650827</span>;</span><br><span class="line"><span class="type">int</span> arr[maxN];</span><br><span class="line"><span class="type">int</span> dp[maxN][maxN][<span class="number">2</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="type">int</span> N;</span><br><span class="line"> cin >> N;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) cin >> arr[i], dp[i][i][<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> len = <span class="number">2</span>; len <= N; len++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i + len - <span class="number">1</span> <= N; i++) {</span><br><span class="line"> <span class="type">int</span> j = i + len - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span> (arr[i] < arr[i + <span class="number">1</span>]) dp[i][j][<span class="number">0</span>] += dp[i + <span class="number">1</span>][j][<span class="number">0</span>] % p;</span><br><span class="line"> <span class="keyword">if</span> (arr[i] < arr[j]) dp[i][j][<span class="number">0</span>] += dp[i + <span class="number">1</span>][j][<span class="number">1</span>] % p;</span><br><span class="line"> <span class="keyword">if</span> (arr[j] > arr[j - <span class="number">1</span>]) dp[i][j][<span class="number">1</span>] += dp[i][j - <span class="number">1</span>][<span class="number">1</span>] % p;</span><br><span class="line"> <span class="keyword">if</span> (arr[j] > arr[i]) dp[i][j][<span class="number">1</span>] += dp[i][j - <span class="number">1</span>][<span class="number">0</span>] % p;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << (dp[<span class="number">1</span>][N][<span class="number">0</span>] + dp[<span class="number">1</span>][N][<span class="number">1</span>]) % p << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Zuma"><a href="https://www.luogu.com.cn/problem/CF607B">Zuma</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">505</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> arr[maxN];</span><br><span class="line"><span class="type">int</span> dp[maxN][maxN];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0x3f</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="type">int</span> N;</span><br><span class="line"> cin >> N;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) cin >> arr[i], dp[i][i] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i < N; i++)</span><br><span class="line"> <span class="keyword">if</span> (arr[i] == arr[i + <span class="number">1</span>]) dp[i][i + <span class="number">1</span>] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">else</span> dp[i][i + <span class="number">1</span>] = <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> len = <span class="number">3</span>; len <= N; len++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i + len - <span class="number">1</span> <= N; i++) {</span><br><span class="line"> <span class="type">int</span> j = i + len - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span> (arr[i] == arr[j]) dp[i][j] = <span class="built_in">min</span>(dp[i][j], dp[i + <span class="number">1</span>][j - <span class="number">1</span>]);</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> t = i; t < j; t++) {</span><br><span class="line"> dp[i][j] = <span class="built_in">min</span>(dp[i][j], dp[i][t] + dp[t + <span class="number">1</span>][j]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << dp[<span class="number">1</span>][N] << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="石子合并4"><a href="https://www.luogu.com.cn/problem/P5569">石子合并4</a></h2><blockquote><p><strong>前置知识: <a href="https://oi-wiki.org/misc/garsia-wachs/">GarsiaWachs</a>算法</strong></p></blockquote><p>在一个操场上摆放着一排 <code>N</code> 堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的 2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。</p><p>试设计一个算法,计算出将 <code>N</code> 堆石子合并成一堆的最小得分。<strong>注意 <code>N<=40000</code></strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h1>定义</h1>
<p>区间类动态规划是线性动态规划的扩展,它在分阶段地划分问题时,与阶段中元素出现的顺序和由前一阶段的哪些元素合并而来有很大的关系。</p>
<p>令状态 <code>f(i,j)</code> 表示将下标位置 <code>i</code> 到 <code</summary>
<category term="算法" scheme="https://creating001.github.io/categories/%E7%AE%97%E6%B3%95/"/>
<category term="动态规划" scheme="https://creating001.github.io/categories/%E7%AE%97%E6%B3%95/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="动态规划" scheme="https://creating001.github.io/tags/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="区间dp" scheme="https://creating001.github.io/tags/%E5%8C%BA%E9%97%B4dp/"/>
</entry>
<entry>
<title>动态规划之线性dp</title>
<link href="https://creating001.github.io/2023/05/16/dp-2-linear/"/>
<id>https://creating001.github.io/2023/05/16/dp-2-linear/</id>
<published>2023-05-15T16:03:20.000Z</published>
<updated>2023-06-02T11:41:58.879Z</updated>
<content type="html"><![CDATA[<h1>概述</h1><p>线性动态规划,是较常见的一类动态规划问题,其是在线性结构上进行状态转移,这类问题不像背包问题、区间DP等有固定的模板。</p><p>线性动态规划的目标函数为特定变量的线性函数,约束是这些变量的线性不等式或等式,目的是求目标函数的最大值或最小值。</p><p>因此,除了少量问题(如LIS、LCS、LCIS等)有固定的模板外,大部分都要根据实际问题来推导得出答案。</p><h1>典型题目</h1><h2 id="LIS"><a href="https://leetcode.cn/problems/longest-increasing-subsequence/">LIS</a></h2><h3 id="线性dp">线性dp</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">int</span> <span class="title">lengthOfLIS</span><span class="params">(vector<<span class="type">int</span>>& nums)</span> </span>{</span><br><span class="line"> <span class="function">vector<<span class="type">int</span>> <span class="title">dp</span><span class="params">(nums.size(), <span class="number">1</span>)</span></span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i < dp.<span class="built_in">size</span>(); i++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < i; j++)</span><br><span class="line"> <span class="keyword">if</span> (nums[i] > nums[j]) dp[i] = <span class="built_in">max</span>(dp[i], dp[j] + <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">return</span> *<span class="built_in">max_element</span>(dp.<span class="built_in">begin</span>(), dp.<span class="built_in">end</span>());</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h3 id="贪心-二分">贪心+二分</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">int</span> <span class="title">lengthOfLIS</span><span class="params">(vector<<span class="type">int</span>>& nums)</span> </span>{</span><br><span class="line"> vector<<span class="type">int</span>> d;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span> x : nums) {</span><br><span class="line"> <span class="keyword">if</span> (d.<span class="built_in">empty</span>() || x > d.<span class="built_in">back</span>()) d.<span class="built_in">emplace_back</span>(x);</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="type">int</span> index = <span class="built_in">lower_bound</span>(d.<span class="built_in">begin</span>(), d.<span class="built_in">end</span>(), x) - d.<span class="built_in">begin</span>();</span><br><span class="line"> d[index] = x;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> d.<span class="built_in">size</span>();</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h2 id="LCS1"><a href="https://leetcode.cn/problems/longest-common-subsequence/">LCS1</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">int</span> <span class="title">longestCommonSubsequence</span><span class="params">(string& text1, string& text2)</span> </span>{</span><br><span class="line"> vector <vector<<span class="type">int</span>>> <span class="built_in">dp</span>(text1.<span class="built_in">size</span>() + <span class="number">1</span>, <span class="built_in">vector</span><<span class="type">int</span>>(text2.<span class="built_in">size</span>() + <span class="number">1</span>));</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= text1.<span class="built_in">size</span>(); i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j <= text2.<span class="built_in">size</span>(); j++) {</span><br><span class="line"> <span class="keyword">if</span> (text1[i - <span class="number">1</span>] == text2[j - <span class="number">1</span>])</span><br><span class="line"> dp[i][j] = dp[i - <span class="number">1</span>][j - <span class="number">1</span>] + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">else</span> dp[i][j] = <span class="built_in">max</span>(dp[i - <span class="number">1</span>][j], dp[i][j - <span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> dp.<span class="built_in">back</span>().<span class="built_in">back</span>();</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h2 id="LCS2"><a href="https://www.luogu.com.cn/problem/P1439">LCS2</a></h2><p>关于为什么可以转化成LIS问题,这里提供一个解释。</p><p>A:3 2 1 4 5</p><p>B:1 2 3 4 5</p><p>我们不妨给它们重新标个号:把3标成a,把2标成b,把1标成c … 于是变成:</p><p>A: a b c d e</p><p>B: c b a d e</p><p>这样标号之后,LCS长度显然不会改变。但是出现了一个性质:</p><p>两个序列的子序列,一定是A的子序列。而A本身就是单调递增的。因此这个子序列是单调递增的。</p><p>换句话说,只要这个子序列在B中单调递增,它就是A的子序列。哪个最长呢?当然是B的LIS最长。自此完成转化。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">std::ios::<span class="built_in">sync_with_stdio</span>(<span class="literal">false</span>);</span><br><span class="line"><span class="type">int</span> n;</span><br><span class="line">cin >> n;</span><br><span class="line"><span class="function">vector<<span class="type">int</span>> <span class="title">arr1</span><span class="params">(n)</span>, <span class="title">arr2</span><span class="params">(n)</span></span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < n; i++) cin >> arr1[i];</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < n; i++) cin >> arr2[i];</span><br><span class="line"><span class="function">vector<<span class="type">int</span>> <span class="title">arr3</span><span class="params">(n)</span></span>;</span><br><span class="line">unordered_map<<span class="type">int</span>, <span class="type">int</span>> map;</span><br><span class="line"><span class="type">int</span> number = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">auto</span>& x : arr1)</span><br><span class="line"> map[x] = number++;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">auto</span>& x : arr2)</span><br><span class="line"> x = map[x];</span><br><span class="line">vector<<span class="type">int</span>> d;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">auto</span> x : arr2) {</span><br><span class="line"> <span class="keyword">if</span> (d.<span class="built_in">empty</span>() || x > d.<span class="built_in">back</span>()) d.<span class="built_in">emplace_back</span>(x);</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="type">int</span> index = <span class="built_in">lower_bound</span>(d.<span class="built_in">begin</span>(), d.<span class="built_in">end</span>(), x) - d.<span class="built_in">begin</span>();</span><br><span class="line"> d[index] = x;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">cout << d.<span class="built_in">size</span>() << endl;</span><br></pre></td></tr></table></figure><h2 id="导弹拦截"><a href="https://www.luogu.com.cn/problem/P1020">导弹拦截</a></h2><blockquote><p><strong>Dilworth 定理: 对于任意有限偏序集,其最大反链中元素的数目必等于最小链划分中链的数目</strong></p></blockquote><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">1e5</span> + <span class="number">5</span>;</span><br><span class="line"><span class="type">int</span> N = <span class="number">0</span>;</span><br><span class="line"><span class="type">int</span> arr[maxN];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">while</span> (cin >> arr[N]) N++;</span><br><span class="line"> vector<<span class="type">int</span>> d;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < N; i++) {</span><br><span class="line"> <span class="keyword">if</span> (d.<span class="built_in">empty</span>() || d.<span class="built_in">back</span>() >= arr[i]) d.<span class="built_in">emplace_back</span>(arr[i]);</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="type">int</span> index = <span class="built_in">upper_bound</span>(d.<span class="built_in">begin</span>(), d.<span class="built_in">end</span>(), arr[i], <span class="built_in">greater</span>())</span><br><span class="line"> - d.<span class="built_in">begin</span>();</span><br><span class="line"> d[index] = arr[i];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << d.<span class="built_in">size</span>() << endl;</span><br><span class="line"> vector<<span class="type">int</span>> p;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < N; i++) {</span><br><span class="line"> <span class="keyword">if</span> (p.<span class="built_in">empty</span>() || p.<span class="built_in">back</span>() < arr[i]) p.<span class="built_in">emplace_back</span>(arr[i]);</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="type">int</span> index = <span class="built_in">lower_bound</span>(p.<span class="built_in">begin</span>(), p.<span class="built_in">end</span>(), arr[i]) - p.<span class="built_in">begin</span>();</span><br><span class="line"> p[index] = arr[i];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << p.<span class="built_in">size</span>() << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="回文字串"><a href="https://www.luogu.com.cn/problem/P1435">回文字串</a></h2><h3 id="解法一">解法一</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">string str;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> dp[<span class="number">1005</span>][<span class="number">1005</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> std::ios::<span class="built_in">sync_with_stdio</span>(<span class="literal">false</span>);</span><br><span class="line"> cin >> str;</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">1</span>; k < str.<span class="built_in">size</span>(); k++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i + k < str.<span class="built_in">size</span>(); i++) {</span><br><span class="line"> <span class="type">int</span> j = i + k;</span><br><span class="line"> <span class="keyword">if</span> (str[i] == str[j]) dp[i][j] = dp[i + <span class="number">1</span>][j - <span class="number">1</span>];</span><br><span class="line"> <span class="keyword">else</span> dp[i][j] = <span class="built_in">min</span>(dp[i + <span class="number">1</span>][j], dp[i][j - <span class="number">1</span>]) + <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << dp[<span class="number">0</span>][str.<span class="built_in">size</span>() - <span class="number">1</span>] << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="解法二">解法二</h3><p>该题说是考察如何将一个字符串添加成一个回文串的,不如说是一道求最长公共自序列的变式题,为啥这么说呢?肯定是有原因在里面的。</p><p>首先,我们要摸清回文串的特性,回文就是正着读反着读一样,一种非常对称不会逼死强迫症的字符串;这就是我们的突破口。你难道以为是逼死强迫症么?哈哈,太天真了,突破口其实是因为回文正着读反着读都相同的特性。这样我们就可以再建一个字符数组存储倒序的字符串。</p><p>先分析样例:ab3bd</p><p>它的倒序是: db3ba</p><p>这样我们就可以把问题转化成了求最长公共自序列的问题,为啥可以这么转化呢?</p><p>它可以这么理解,正序与倒序“公共”的部分就是我们回文的部分,如果把正序与倒序公共的部分减去你就会惊奇的发现剩余的字符就是你所要添加的字符,也就是所求的正解。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">string str1;</span><br><span class="line"><span class="type">int</span> dp[<span class="number">1005</span>][<span class="number">1005</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> std::ios::<span class="built_in">sync_with_stdio</span>(<span class="literal">false</span>);</span><br><span class="line"> cin >> str1;</span><br><span class="line"> string str2;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = str1.<span class="built_in">size</span>() - <span class="number">1</span>; i >= <span class="number">0</span>; i--) str2.<span class="built_in">push_back</span>(str1[i]);</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= str1.<span class="built_in">size</span>(); i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j <= str2.<span class="built_in">size</span>(); j++) {</span><br><span class="line"> <span class="keyword">if</span> (str1[i - <span class="number">1</span>] == str2[j - <span class="number">1</span>]) dp[i][j] = dp[i - <span class="number">1</span>][j - <span class="number">1</span>] + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">else</span> dp[i][j] = <span class="built_in">max</span>(dp[i][j - <span class="number">1</span>], dp[i - <span class="number">1</span>][j]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << str1.<span class="built_in">size</span>() - dp[str1.<span class="built_in">size</span>()][str2.<span class="built_in">size</span>()] << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="乌龟棋"><a href="https://www.luogu.com.cn/problem/P1541">乌龟棋</a></h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> N, M;</span><br><span class="line"><span class="type">int</span> score[<span class="number">355</span>];</span><br><span class="line"><span class="type">int</span> card[<span class="number">5</span>];</span><br><span class="line"><span class="type">int</span> dp[<span class="number">45</span>][<span class="number">45</span>][<span class="number">45</span>][<span class="number">45</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> cin >> N >> M;</span><br><span class="line"> <span class="built_in">memset</span>(score, <span class="number">0</span>, <span class="built_in">sizeof</span>(score));</span><br><span class="line"> <span class="built_in">memset</span>(card, <span class="number">0</span>, <span class="built_in">sizeof</span>(card));</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < N; i++) cin >> score[i];</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < M; i++) {</span><br><span class="line"> <span class="type">int</span> temp;</span><br><span class="line"> cin >> temp;</span><br><span class="line"> card[temp]++;</span><br><span class="line"> }</span><br><span class="line"> dp[<span class="number">0</span>][<span class="number">0</span>][<span class="number">0</span>][<span class="number">0</span>] = score[<span class="number">0</span>];</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i <= card[<span class="number">1</span>]; i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j <= card[<span class="number">2</span>]; j++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">0</span>; k <= card[<span class="number">3</span>]; k++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> t = <span class="number">0</span>; t <= card[<span class="number">4</span>]; t++) {</span><br><span class="line"> <span class="type">int</span> index = i + j * <span class="number">2</span> + k * <span class="number">3</span> + t * <span class="number">4</span>;</span><br><span class="line"> <span class="keyword">if</span> (i > <span class="number">0</span>)</span><br><span class="line"> dp[i][j][k][t] = <span class="built_in">max</span>(dp[i][j][k][t], dp[i - <span class="number">1</span>][j][k][t] + score[index]);</span><br><span class="line"> <span class="keyword">if</span> (j > <span class="number">0</span>)</span><br><span class="line"> dp[i][j][k][t] = <span class="built_in">max</span>(dp[i][j][k][t], dp[i][j - <span class="number">1</span>][k][t] + score[index]);</span><br><span class="line"> <span class="keyword">if</span> (k > <span class="number">0</span>)</span><br><span class="line"> dp[i][j][k][t] = <span class="built_in">max</span>(dp[i][j][k][t], dp[i][j][k - <span class="number">1</span>][t] + score[index]);</span><br><span class="line"> <span class="keyword">if</span> (t > <span class="number">0</span>)</span><br><span class="line"> dp[i][j][k][t] = <span class="built_in">max</span>(dp[i][j][k][t], dp[i][j][k][t - <span class="number">1</span>] + score[index]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="type">int</span> ans = dp[card[<span class="number">1</span>]][card[<span class="number">2</span>]][card[<span class="number">3</span>]][card[<span class="number">4</span>]];</span><br><span class="line"> cout << ans << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Negatives-and-Positives"><a href="https://codeforces.com/contest/1791/problem/E">Negatives and Positives</a></h2><h3 id="线性dp-状态记录">线性dp+状态记录</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">2e5</span> + <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">long</span> <span class="type">long</span> arr[maxN];</span><br><span class="line"><span class="type">long</span> <span class="type">long</span> dp[<span class="number">2</span>][maxN];<span class="comment">//两个状态</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> std::ios::<span class="built_in">sync_with_stdio</span>(<span class="literal">false</span>);</span><br><span class="line"> <span class="type">int</span> t;</span><br><span class="line"> cin >> t;</span><br><span class="line"> <span class="keyword">while</span> (t--) {</span><br><span class="line"> <span class="type">int</span> size;</span><br><span class="line"> cin >> size;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < size; i++) cin >> arr[i];</span><br><span class="line"> dp[<span class="number">0</span>][<span class="number">0</span>] = arr[<span class="number">0</span>];</span><br><span class="line"> dp[<span class="number">1</span>][<span class="number">0</span>] = -arr[<span class="number">0</span>];</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i < size; i++) {</span><br><span class="line"> dp[<span class="number">0</span>][i] = <span class="built_in">max</span>(dp[<span class="number">0</span>][i - <span class="number">1</span>] + arr[i], dp[<span class="number">1</span>][i - <span class="number">1</span>] - arr[i]);</span><br><span class="line"> dp[<span class="number">1</span>][i] = <span class="built_in">max</span>(dp[<span class="number">0</span>][i - <span class="number">1</span>] - arr[i], dp[<span class="number">1</span>][i - <span class="number">1</span>] + arr[i]);</span><br><span class="line"> }</span><br><span class="line"> cout << dp[<span class="number">0</span>][size - <span class="number">1</span>] << endl;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="排序-数学归纳">排序+数学归纳</h3><p>由题意易知我们可以将任意两个数都进行取相反数, 这里不做证明。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">2e5</span> + <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">long</span> <span class="type">long</span> arr[maxN];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> std::ios::<span class="built_in">sync_with_stdio</span>(<span class="literal">false</span>);</span><br><span class="line"> <span class="type">int</span> t;</span><br><span class="line"> cin >> t;</span><br><span class="line"> <span class="keyword">while</span> (t--) {</span><br><span class="line"> <span class="type">int</span> size;</span><br><span class="line"> cin >> size;</span><br><span class="line"> <span class="type">long</span> <span class="type">long</span> ans = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < size; i++) cin >> arr[i], ans += arr[i];</span><br><span class="line"> <span class="built_in">sort</span>(arr, arr + size);</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < size - <span class="number">1</span>; i += <span class="number">2</span>) {</span><br><span class="line"> <span class="keyword">if</span> (arr[i] + arr[i + <span class="number">1</span>] < <span class="number">0</span>) {</span><br><span class="line"> ans = ans + <span class="number">2</span> * (-arr[i] - arr[i + <span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << ans << endl;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Save-the-Magazines"><a href="https://codeforces.com/contest/1743/problem/C">Save the Magazines</a></h2><h3 id="线性dp-状态记录-2">线性dp+状态记录</h3><p>定义状态f(i,0)和f(i,1),其中f(i,0)表示所有将前i个箱子的盖子移动完且第i个箱子没有使用第i+1个箱子的盖子的所有方案,f(i,1)表示所有将前i个箱子的盖子移动完且第i个箱子使用第i+1个箱子的盖子的所有方案。属性就是保护的杂志的最大数量。</p><p>状态转移方程需要分类讨论:</p><ol><li>第i个箱子原本没有盖子,且第i+1个箱子也没有盖子,那么有<code>f(i,0)=f(i−1,0)</code>。</li><li>第i个箱子原本有盖子,且第i+1个箱子没有盖子,那么有<code>f(i,0)=max{f(i−1,0)+ai,f(i−1,1)}</code>。</li><li>第i个箱子原本没有盖子,且第i+1个箱子有盖子,那么有<code>f(i,0)=f(i−1,0)</code>,<code>f(i,1)=f(i−1,0)+ai</code>。</li><li>第i个箱子原本有盖子,且第i+1个箱子有盖子,那么有<code>f(i,0)=max{f(i−1,0)+ai,f(i−1,1)}</code>, <code>f(i,1)=f(i−1,1)+ai</code>。</li></ol><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">2e5</span> + <span class="number">5</span>;</span><br><span class="line"><span class="type">int</span> dp[maxN][<span class="number">2</span>];</span><br><span class="line"><span class="type">char</span> str[maxN];</span><br><span class="line"><span class="type">int</span> arr[maxN];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> std::ios::<span class="built_in">sync_with_stdio</span>(<span class="literal">false</span>);</span><br><span class="line"> <span class="type">int</span> t;</span><br><span class="line"> cin >> t;</span><br><span class="line"> <span class="keyword">while</span> (t--) {</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> dp[<span class="number">0</span>][<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> <span class="type">int</span> n;</span><br><span class="line"> cin >> n;</span><br><span class="line"> cin >> (str + <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= n; i++) cin >> arr[i];</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= n; i++) {</span><br><span class="line"> <span class="keyword">if</span> (str[i] == <span class="string">'0'</span>) {</span><br><span class="line"> dp[i][<span class="number">0</span>] = dp[i - <span class="number">1</span>][<span class="number">0</span>];</span><br><span class="line"> <span class="keyword">if</span> (str[i + <span class="number">1</span>] == <span class="string">'1'</span>) {</span><br><span class="line"> dp[i][<span class="number">1</span>] = dp[i - <span class="number">1</span>][<span class="number">0</span>] + arr[i];</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> dp[i][<span class="number">0</span>] = <span class="built_in">max</span>(dp[i - <span class="number">1</span>][<span class="number">1</span>], dp[i - <span class="number">1</span>][<span class="number">0</span>] + arr[i]);</span><br><span class="line"> <span class="keyword">if</span> (str[i + <span class="number">1</span>] == <span class="string">'1'</span>) {</span><br><span class="line"> dp[i][<span class="number">1</span>] = dp[i - <span class="number">1</span>][<span class="number">1</span>] + arr[i];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << dp[n][<span class="number">0</span>] << endl;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="贪心">贪心</h3><p>我们将s序列分成若干段,每一段的形式都是第一个数是0,后面都是连续的1(可能只有一个0而没有1)。如果<code>s[0]</code>不为0,那么第一段就只包含连续的1。比如有<code>111010011</code>,那么按照这个规则s划分成如下形式:<code>[111] [01] [0] [011]</code>。</p><p>为什么会想到这样子划分呢?因为根据题目的信息,每个盖子只能被移动一次,这样划分就可以保证每一段之间都是相互独立的,因为每一段的第一个位置为0(第一段除外),因此如果这个0被这一段后面的盖子覆盖后,由于这个盖子已经移动过一次了,因此不可能再往前移动,因此每一段都是互不影响的。</p><p>因此要求出所有箱子能保护的杂志最大数量,等价于求每一段中能够保存的杂志最大数量。可以发现对于 <code>0 1 1 ... 1</code> 这种形式,通过往前移动盖子我们可以让0出现在任意一个位置,因此要使得这段中能保护的杂志数量最大,就是累加这一段中所有箱子存放的杂志数量,再减去存放最小杂志数量的箱子所存放的杂志(也就是说把0变到存放杂志数量最少的箱子上)。</p><p>为了方便,一开始就给序列s头插一个0,保证<code>s[0]=0</code>。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">2e5</span> + <span class="number">5</span>;</span><br><span class="line"><span class="type">char</span> str[maxN];</span><br><span class="line"><span class="type">int</span> arr[maxN];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> std::ios::<span class="built_in">sync_with_stdio</span>(<span class="literal">false</span>);</span><br><span class="line"> <span class="type">int</span> t;</span><br><span class="line"> cin >> t;</span><br><span class="line"> <span class="keyword">while</span> (t--) {</span><br><span class="line"> <span class="type">int</span> n;</span><br><span class="line"> cin >> n >> (str + <span class="number">1</span>);</span><br><span class="line"> str[<span class="number">0</span>] = <span class="string">'0'</span>, arr[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= n; i++) cin >> arr[i];</span><br><span class="line"> <span class="type">int</span> ans = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i <= n; i++) {</span><br><span class="line"> <span class="keyword">if</span> (str[i] == <span class="string">'0'</span>) {</span><br><span class="line"> <span class="type">int</span> sum = arr[i], min = arr[i];</span><br><span class="line"> <span class="type">int</span> j = i + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">while</span> (j <= n && str[j] == <span class="string">'1'</span>) {</span><br><span class="line"> min = std::<span class="built_in">min</span>(min, arr[j]);</span><br><span class="line"> sum += arr[j];</span><br><span class="line"> j++;</span><br><span class="line"> }</span><br><span class="line"> ans += sum - min;</span><br><span class="line"> i = j - <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << ans << endl;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h1>概述</h1>
<p>线性动态规划,是较常见的一类动态规划问题,其是在线性结构上进行状态转移,这类问题不像背包问题、区间DP等有固定的模板。</p>
<p>线性动态规划的目标函数为特定变量的线性函数,约束是这些变量的线性不等式或等式,目的是求目标函数的最大值或最小值。</</summary>
<category term="算法" scheme="https://creating001.github.io/categories/%E7%AE%97%E6%B3%95/"/>
<category term="动态规划" scheme="https://creating001.github.io/categories/%E7%AE%97%E6%B3%95/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="动态规划" scheme="https://creating001.github.io/tags/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="线性dp" scheme="https://creating001.github.io/tags/%E7%BA%BF%E6%80%A7dp/"/>
</entry>
<entry>
<title>动态规划之背包问题</title>
<link href="https://creating001.github.io/2023/05/13/dp-1-knapsack/"/>
<id>https://creating001.github.io/2023/05/13/dp-1-knapsack/</id>
<published>2023-05-13T01:30:10.000Z</published>
<updated>2023-06-02T11:42:32.356Z</updated>
<content type="html"><![CDATA[<h1>01背包</h1><p><a href="https://www.acwing.com/problem/content/2/">01背包问题</a></p><p>有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。</p><p>第 i 件物品的体积是 vi ,价值是 wi 。</p><p>求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> n;</span><br><span class="line"><span class="type">int</span> v;</span><br><span class="line">cin >> n >> v;</span><br><span class="line"><span class="function">vector<<span class="type">int</span>> <span class="title">arrV</span><span class="params">(n)</span></span>;</span><br><span class="line"><span class="function">vector<<span class="type">int</span>> <span class="title">arrW</span><span class="params">(n)</span></span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < n; i++) {</span><br><span class="line"> cin >> arrV[i];</span><br><span class="line"> cin >> arrW[i];</span><br><span class="line">}</span><br><span class="line"><span class="function">vector<<span class="type">int</span>> <span class="title">dp</span><span class="params">(v + <span class="number">1</span>, <span class="number">0</span>)</span></span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= n; i++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = v; j >= arrV[i - <span class="number">1</span>]; j--)</span><br><span class="line"> dp[j] = <span class="built_in">max</span>(dp[j], arrW[i - <span class="number">1</span>] + dp[j - arrV[i - <span class="number">1</span>]]);</span><br><span class="line">cout << dp.<span class="built_in">back</span>();</span><br></pre></td></tr></table></figure><h1>完全背包</h1><p><a href="https://www.acwing.com/problem/content/3/">完全背包问题</a></p><p>有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。</p><p>第 i 种物品的体积是 vi ,价值是 wi 。</p><p>求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> n, v;</span><br><span class="line">cin >> n >> v;</span><br><span class="line"><span class="function">vector<<span class="type">int</span>> <span class="title">arrV</span><span class="params">(n)</span></span>;</span><br><span class="line"><span class="function">vector<<span class="type">int</span>> <span class="title">arrW</span><span class="params">(n)</span></span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < n; i++) {</span><br><span class="line"> cin >> arrV[i];</span><br><span class="line"> cin >> arrW[i];</span><br><span class="line">}</span><br><span class="line"><span class="function">vector<<span class="type">int</span>> <span class="title">dp</span><span class="params">(v + <span class="number">1</span>, <span class="number">0</span>)</span></span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= n; i++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = arrV[i - <span class="number">1</span>]; j <= v; j++)</span><br><span class="line"> dp[j] = <span class="built_in">max</span>(dp[j], arrW[i - <span class="number">1</span>] + dp[j - arrV[i - <span class="number">1</span>]]);</span><br><span class="line">cout << dp.<span class="built_in">back</span>();</span><br></pre></td></tr></table></figure><h1>多重背包</h1><p>有 N 种物品和一个容量是 V 的背包。</p><p>第 i 种物品最多有 si 件,每件体积是 vi ,价值是 wi。</p><p>求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。</p><h2 id="朴素解法">朴素解法</h2><p><a href="https://www.acwing.com/problem/content/4/">多重背包问题1</a></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> n, v;</span><br><span class="line">cin >> n >> v;</span><br><span class="line"><span class="function">vector<<span class="type">int</span>> <span class="title">arrV</span><span class="params">(n)</span></span>;</span><br><span class="line"><span class="function">vector<<span class="type">int</span>> <span class="title">arrW</span><span class="params">(n)</span></span>;</span><br><span class="line"><span class="function">vector<<span class="type">int</span>> <span class="title">arrS</span><span class="params">(n)</span></span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < n; i++) {</span><br><span class="line"> cin >> arrV[i];</span><br><span class="line"> cin >> arrW[i];</span><br><span class="line"> cin >> arrS[i];</span><br><span class="line">}</span><br><span class="line"><span class="function">vector<<span class="type">int</span>> <span class="title">dp</span><span class="params">(v + <span class="number">1</span>, <span class="number">0</span>)</span></span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= n; i++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < arrS[i - <span class="number">1</span>]; j++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = v; k >= arrV[i - <span class="number">1</span>]; k--)</span><br><span class="line"> dp[k] = <span class="built_in">max</span>(dp[k], arrW[i - <span class="number">1</span>] + dp[k - arrV[i - <span class="number">1</span>]]);</span><br><span class="line">cout << dp.<span class="built_in">back</span>();</span><br></pre></td></tr></table></figure><h2 id="二进制优化">二进制优化</h2><p><a href="https://www.acwing.com/problem/content/5/">多重背包问题2</a></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> N, V;</span><br><span class="line">cin >> N >> V;</span><br><span class="line"><span class="function">vector<<span class="type">int</span>> <span class="title">dp</span><span class="params">(V+<span class="number">1</span>)</span></span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < N; i++) {</span><br><span class="line"> <span class="type">int</span> v, w, s;</span><br><span class="line"> cin >> v >> w >> s;</span><br><span class="line"> <span class="type">int</span> k = <span class="number">1</span>;</span><br><span class="line"> vector<<span class="type">int</span>> arrV;</span><br><span class="line"> vector<<span class="type">int</span>> arrW;</span><br><span class="line"> <span class="keyword">while</span> (s >= k) {</span><br><span class="line"> arrV.<span class="built_in">emplace_back</span>(v * k);</span><br><span class="line"> arrW.<span class="built_in">emplace_back</span>(w * k);</span><br><span class="line"> s -= k;</span><br><span class="line"> k *= <span class="number">2</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (s > <span class="number">0</span>) {</span><br><span class="line"> arrV.<span class="built_in">emplace_back</span>(v * s);</span><br><span class="line"> arrW.<span class="built_in">emplace_back</span>(w * s);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < arrV.<span class="built_in">size</span>(); j++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> z = V; z >= arrV[j]; z--) {</span><br><span class="line"> dp[z] = <span class="built_in">max</span>(dp[z], dp[z - arrV[j]] + arrW[j]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">cout << dp.<span class="built_in">back</span>();</span><br></pre></td></tr></table></figure><h2 id="单调队列优化">单调队列优化</h2><p><a href="https://www.acwing.com/problem/content/6/">多重背包问题3</a></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> q[<span class="number">50005</span>];<span class="comment">//单调队列</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">int</span> N, V;</span><br><span class="line"> cin >> N >> V;</span><br><span class="line"> <span class="comment">//一个存现在状态,一个存上一个状态</span></span><br><span class="line"> vector<vector<<span class="type">int</span>>> <span class="built_in">dp</span>(<span class="number">2</span>, <span class="built_in">vector</span><<span class="type">int</span>>(V + <span class="number">1</span>));</span><br><span class="line"> <span class="type">int</span> pre = <span class="number">0</span>;</span><br><span class="line"> <span class="type">int</span> cur = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < N; i++) {</span><br><span class="line"> pre = <span class="number">1</span> - pre;</span><br><span class="line"> cur = <span class="number">1</span> - cur;</span><br><span class="line"> <span class="type">int</span> v, w, s;</span><br><span class="line"> cin >> v >> w >> s;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < v; j++) {</span><br><span class="line"> <span class="type">int</span> head = <span class="number">0</span>;</span><br><span class="line"> <span class="type">int</span> tail = <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = j; k <= V; k += v) {</span><br><span class="line"> dp[cur][k] = dp[pre][k];</span><br><span class="line"> <span class="keyword">while</span> (head <= tail && q[head] < k - v * s) head++;</span><br><span class="line"> <span class="keyword">if</span> (head <= tail)</span><br><span class="line"> dp[cur][k] = <span class="built_in">max</span>(dp[cur][k], dp[pre][q[head]] + (k - q[head]) / v * w);</span><br><span class="line"> <span class="keyword">while</span> (head <= tail && dp[pre][q[tail]] + (k - q[tail]) / v * w <= dp[pre][k])</span><br><span class="line"> tail--;</span><br><span class="line"> q[++tail] = k;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << dp[cur][V];</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1>混合背包</h1><p><a href="https://www.acwing.com/problem/content/7/">混合背包问题</a></p><p>有 N 种物品和一个容量是 V 的背包。</p><p>物品一共有三类:</p><ol><li>第一类物品只能用1次(01背包);</li><li>第二类物品可以用无限次(完全背包);</li><li>第三类物品最多只能用 si 次(多重背包);<br>每种体积是 vi,价值是 wi。</li></ol><p>求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。输出最大价值。</p><h2 id="题解代码">题解代码</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> N, V;</span><br><span class="line">cin >> N >> V;</span><br><span class="line">vector<vector<<span class="type">int</span>>> <span class="built_in">dp</span>(<span class="number">2</span>, <span class="built_in">vector</span><<span class="type">int</span>>(V + <span class="number">1</span>));</span><br><span class="line"><span class="type">int</span> cur = <span class="number">0</span>;</span><br><span class="line"><span class="type">int</span> pre = <span class="number">1</span>;</span><br><span class="line"><span class="type">int</span> q[<span class="number">5005</span>];<span class="comment">//单调队列</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < N; i++) {</span><br><span class="line"> pre = <span class="number">1</span> - pre;</span><br><span class="line"> cur = <span class="number">1</span> - cur;</span><br><span class="line"> <span class="type">int</span> v, w, s;</span><br><span class="line"> cin >> v >> w >> s;</span><br><span class="line"> <span class="keyword">if</span> (s == <span class="number">-1</span>) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < v; j++) dp[cur][j] = dp[pre][j];</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = v; j <= V; j++) {</span><br><span class="line"> dp[cur][j] = <span class="built_in">max</span>(dp[pre][j], dp[pre][j - v] + w);</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (s == <span class="number">0</span>) {</span><br><span class="line"> cur = <span class="number">1</span> - cur;</span><br><span class="line"> pre = <span class="number">1</span> - pre;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = v; j <= V; j++) {</span><br><span class="line"> dp[cur][j] = <span class="built_in">max</span>(dp[cur][j], dp[cur][j - v] + w);</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < v; j++) {</span><br><span class="line"> <span class="type">int</span> head = <span class="number">0</span>;</span><br><span class="line"> <span class="type">int</span> tail = <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = j; k <= V; k += v) {</span><br><span class="line"> dp[cur][k] = dp[pre][k];</span><br><span class="line"> <span class="keyword">while</span> (head <= tail && q[head] < k - s * v) head++;</span><br><span class="line"> <span class="keyword">if</span> (head <= tail) {</span><br><span class="line"> dp[cur][k] = <span class="built_in">max</span>(dp[cur][k], dp[pre][q[head]] + (k - q[head]) / v * w);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">while</span> (head <= tail && dp[pre][q[tail]] + (k - q[tail]) / v * w <= dp[pre][k])</span><br><span class="line"> tail--;</span><br><span class="line"> q[++tail] = k;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">cout << dp[cur].<span class="built_in">back</span>() << endl;</span><br></pre></td></tr></table></figure><h1>二维费用背包</h1><p><a href="https://www.acwing.com/problem/content/8/">二维费用背包问题</a></p><p>有 N 件物品和一个容量是 V 的背包,背包能承受的最大重量是 M。</p><p>每件物品只能用一次。体积是 vi,重量是 mi,价值是 wi。</p><p>求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。输出最大价值。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> dp[<span class="number">105</span>][<span class="number">105</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">int</span> N, V, M;</span><br><span class="line"> cin >> N >> V >> M;</span><br><span class="line"> <span class="built_in">memset</span>(dp,<span class="number">0</span>,<span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>;i < N;i++) {</span><br><span class="line"> <span class="type">int</span> v, m, w;</span><br><span class="line"> cin >> v >> m >> w;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = V;j >= v;j--) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = M;k >= m;k--) {</span><br><span class="line"> dp[j][k] = <span class="built_in">max</span>(dp[j][k], dp[j - v][k - m] + w);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << dp[V][M];</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1>分组背包</h1><p><a href="https://www.acwing.com/problem/content/9/">分组背包问题</a></p><p>有 N 组物品和一个容量是 V 的背包。</p><p>每组物品有若干个,同一组内的物品最多只能选一个。每件物品的体积是 vij ,价值是 wij ,其中 i是组号,j是组内编号。</p><p>求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。输出最大价值。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> dp[<span class="number">105</span>];</span><br><span class="line"><span class="type">int</span> v[<span class="number">105</span>];</span><br><span class="line"><span class="type">int</span> w[<span class="number">105</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">int</span> N, V;</span><br><span class="line"> cin >> N >> V;</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>;i < N;i++) {</span><br><span class="line"> <span class="type">int</span> s;</span><br><span class="line"> cin >> s;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>;j < s;j++) {</span><br><span class="line"> cin >> v[j] >> w[j];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = V;j >= <span class="number">0</span>;j--) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">0</span>;k < s;k++) {</span><br><span class="line"> <span class="keyword">if</span> (j >= v[k]) dp[j] = <span class="built_in">max</span>(dp[j], dp[j - v[k]] + w[k]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << dp[V] << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1>有依赖的背包</h1><p><a href="https://www.acwing.comv/problem/content/10/">有依赖的背包问题</a></p><p>有 N 个物品和一个容量是 V 的背包。</p><p>物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。</p><p>每件物品的编号是 i,体积是 vi,价值是 wi,依赖的父节点编号是 pi。物品的下标范围是 <code>1...N</code>。</p><p>求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。输出最大价值。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxN = <span class="number">105</span>;</span><br><span class="line"></span><br><span class="line">vector<<span class="type">int</span>> arr[maxN];</span><br><span class="line"><span class="type">int</span> dp[maxN][maxN];</span><br><span class="line"><span class="type">int</span> arrV[maxN];</span><br><span class="line"><span class="type">int</span> arrW[maxN];</span><br><span class="line"><span class="type">int</span> vis[maxN];</span><br><span class="line"><span class="type">int</span> N, V;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> s)</span> </span>{</span><br><span class="line"> vis[s] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = V; i >= arrV[s]; i--) dp[s][i] = arrW[s];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span> u : arr[s]) {</span><br><span class="line"> <span class="keyword">if</span> (vis[u]) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="built_in">dfs</span>(u);</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = V; i >= arrV[s]; i--)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; i - j >= arrV[s]; j++)</span><br><span class="line"> dp[s][i] = <span class="built_in">max</span>(dp[s][i], dp[s][i - j] + dp[u][j]);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="built_in">memset</span>(vis, <span class="number">0</span>, <span class="built_in">sizeof</span>(vis));</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="type">int</span> root;</span><br><span class="line"> cin >> N >> V;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) {</span><br><span class="line"> <span class="type">int</span> p;</span><br><span class="line"> cin >> arrV[i] >> arrW[i] >> p;</span><br><span class="line"> <span class="keyword">if</span> (p == <span class="number">-1</span>) root = i;</span><br><span class="line"> <span class="keyword">else</span> arr[p].<span class="built_in">emplace_back</span>(i);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">dfs</span>(root);</span><br><span class="line"> cout << dp[root][V] << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1>泛化物品的背包</h1><p>这种背包,没有固定的费用和价值,它的价值是随着分配给它的费用而定。在背包容量为 <code>V</code> 的背包问题中,当分配给它的费用为 <code>vi</code> 时,能得到的价值就是 <code>h(vi)</code>。这时,将固定的价值换成函数的引用即可。</p><p><a href="https://www.luogu.com.cn/problem/P1336">泛化物品的背包问题</a></p><p>Matrix 要在下个月交给老师 <code>n</code> 篇论文,论文的内容可以从 <code>m</code> 个课题中选择。由于课题数有限,Matrix 不得不重复选择一些课题。完成不同课题的论文所花的时间不同。具体地说,对于某个课题 <code>i</code>,若 Matrix 计划一共写 <code>x</code> 篇论文,则完成该课题的论文总共需要花费 <code>ai * X^bi</code> 个单位时间。给定与每一个课题相对应的 <code>ai</code> 和 <code>bi</code> 的值,请帮助 Matrix 计算出如何选择论文的课题使得他可以花费最少的时间完成这 <code>n</code> 篇论文。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">node</span> {</span><br><span class="line"> <span class="type">int</span> a;</span><br><span class="line"> <span class="type">int</span> b;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">F</span><span class="params">(<span class="type">const</span> node& n, <span class="type">long</span> <span class="type">long</span> x)</span> </span>{</span><br><span class="line"> <span class="type">long</span> <span class="type">long</span> ret = <span class="number">1</span>;</span><br><span class="line"> <span class="type">int</span> exp = n.b;</span><br><span class="line"> <span class="keyword">while</span> (exp > <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">if</span> (exp & <span class="number">1</span>) ret *= x;</span><br><span class="line"> x = x * x;</span><br><span class="line"> exp = exp >> <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ret * n.a;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> N, M;</span><br><span class="line">node arr[<span class="number">24</span>];</span><br><span class="line"><span class="type">long</span> <span class="type">long</span> dp[<span class="number">205</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> cin >> N >> M;</span><br><span class="line"> dp[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++)</span><br><span class="line"> dp[i] = INT_MAX;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < M; i++)</span><br><span class="line"> cin >> arr[i].a >> arr[i].b;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < M; i++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = N; j >= <span class="number">1</span>; j--)</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">1</span>; k <= j; k++)</span><br><span class="line"> dp[j] = <span class="built_in">min</span>(dp[j], dp[j - k] + <span class="built_in">F</span>(arr[i], k));</span><br><span class="line"> cout << dp[N] << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1>背包问题问法的变化</h1><p>以上涉及的各种背包问题都是要求在背包容量(费用)的限制下求可以取到的最大价值,但背包问题还有很多种灵活的问法,在这里值得提一下。但是我认为,只要深入理解了求背包问题最大价值的方法,即使问法变化了,也是不难想出算法的。</p><h2 id="输出方案">输出方案</h2><p>一般而言,背包问题是要求一个最优值,如果要求输出这个最优值的方案,可以参照一般动态规划问题输出方案的方法:记录下每个状态的最优值是由状态转移方程的哪一项推出来的,换句话说,记录下它是由哪一个策略推出来的。便可根据这条策略找到上一个状态,从上一个状态接着向前推即可。</p><p>还是以01背包为例,方程为<code>f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}</code>。再用一个数组<code>g[i][v]</code>,设<code>g[i][v]=0</code>表示推出<code>f[i][v]</code>的值时是采用了方程的前一项(也即<code>f[i][v]=f[i-1][v]</code>),<code>g[i][v]</code>表示采用了方程的后一项。注意这两项分别表示了两种策略:未选第i个物品及选了第i个物品。</p><h2 id="输出字典序最小的最优方案">输出字典序最小的最优方案</h2><p>这里“字典序最小”的意思是<code>1...N</code>号物品的选择方案排列出来以后字典序最小。以输出01背包最小字典序的方案为例。</p><p>一般而言,求一个字典序最小的最优方案,只需要在转移时注意策略。首先,子问题的定义要略改一些。我们注意到,如果存在一个选了物品1的最优方案,那么答案一定包含物品1,原问题转化为一个背包容量为<code>v-c[1]</code>,物品为<code>2..N</code>的子问题。反之,如果答案不包含物品1,则转化成背包容量仍为V,物品为<code>2..N</code>的子问题。不管答案怎样,子问题的物品都是以<code>i..N</code>而非前所述的<code>1..i</code>的形式来定义的,所以状态的定义和转移方程都需要改一下。但也许更简易的方法是先把物品逆序排列一下,以下按物品已被逆序排列来叙述。</p><p>在这种情况下,可以按照前面经典的状态转移方程来求值,只是输出方案的时候要注意:从N到1输入时,如果<code>f[i][v]==f[i-1][i-v]</code>及<code>f[i][v]==f[i-1][f-c[i]]+w[i]</code>同时成立,应该按照后者(即选择了物品i)来输出方案。</p><h3 id="典型例题">典型例题</h3><p><a href="https://www.acwing.com/problem/content/12/">输出字典序最小的最优方案</a></p><p>题目要求输出字典序最小的解,假设存在一个包含第1个物品的最优解,为了确保字典序最小那么我们必然要选第一个。那么问题就转化成从<code>2~N</code>这些物品中找到最优解。</p><p>之前的<code>f(i,j)</code> 记录的都是前<code>i</code> 个物品总容量为<code>j</code> 的最优解,那么我们现在将<code>f(i,j)</code> 定义为从第<code>i</code> 个元素到最后一个元素总容量为<code>j</code> 的最优解。</p><p>接下来考虑状态转移:</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>i</mi><mo separator="true">,</mo><mi>j</mi><mo stretchy="false">)</mo><mo>=</mo><mi>m</mi><mi>a</mi><mi>x</mi><mo stretchy="false">(</mo><mi>f</mi><mo stretchy="false">(</mo><mi>i</mi><mo>+</mo><mn>1</mn><mo separator="true">,</mo><mi>j</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mi>f</mi><mo stretchy="false">(</mo><mi>i</mi><mo>+</mo><mn>1</mn><mo separator="true">,</mo><mi>j</mi><mo>−</mo><mi>v</mi><mo stretchy="false">[</mo><mi>i</mi><mo stretchy="false">]</mo><mo stretchy="false">)</mo><mo>+</mo><mi>w</mi><mo stretchy="false">[</mo><mi>i</mi><mo stretchy="false">]</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">f(i,j)=max(f(i+1,j),f(i+1,j−v[i])+w[i]) </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mopen">(</span><span class="mord mathnormal">i</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05724em;">j</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ma</span><span class="mord mathnormal">x</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mopen">(</span><span class="mord mathnormal">i</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">1</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05724em;">j</span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mopen">(</span><span class="mord mathnormal">i</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.854em;vertical-align:-0.1944em;"></span><span class="mord">1</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05724em;">j</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="mopen">[</span><span class="mord mathnormal">i</span><span class="mclose">])</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mopen">[</span><span class="mord mathnormal">i</span><span class="mclose">])</span></span></span></span></span></p><p>两种情况,第一种是不选第i 个物品,那么最优解等同于从第<code>i+1</code> 个物品到最后一个元素总容量为<code>j</code> 的最优解;第二种是选了第<code>i</code> 个物品,那么最优解等于当前物品的价值<code>w[i]</code> 加上从第<code>i+1</code> 个物品到最后一个元素总容量为<code>j−v[i]</code> 的最优解。</p><p>计算完状态表示后,考虑如何的到最小字典序的解。首先<code>f(1,m)</code> 肯定是最大价值,那么我们便开始考虑能否选取第1个物品呢。</p><ul><li>如果<code>f(1,m)=f(2,m−v[1])+w[1]</code> ,说明选取了第1个物品可以得到最优解。</li><li>如果<code>f(1,m)=f(2,m)</code> ,说明不选取第一个物品才能得到最优解。</li><li>如果<code>f(1,m)=f(2,m)=f(2,m−v[1])+w[1]</code> ,说明选不选都可以得到最优解,但是为了考虑字典序最小,我们也需要选取该物品。</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> N, V;</span><br><span class="line"><span class="type">int</span> arrV[<span class="number">1005</span>];</span><br><span class="line"><span class="type">int</span> arrW[<span class="number">1005</span>];</span><br><span class="line"><span class="type">int</span> dp[<span class="number">1005</span>][<span class="number">1005</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> cin >> N >> V;</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="built_in">memset</span>(arrV, <span class="number">0</span>, <span class="built_in">sizeof</span>(arrV));</span><br><span class="line"> <span class="built_in">memset</span>(arrW, <span class="number">0</span>, <span class="built_in">sizeof</span>(arrW));</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) {</span><br><span class="line"> cin >> arrV[i] >> arrW[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = N; i >= <span class="number">1</span>; i--) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j <= V; j++) {</span><br><span class="line"> dp[i][j] = dp[i + <span class="number">1</span>][j];</span><br><span class="line"> <span class="keyword">if</span> (j >= arrV[i])</span><br><span class="line"> dp[i][j] = <span class="built_in">max</span>(dp[i][j], dp[i + <span class="number">1</span>][j - arrV[i]] + arrW[i]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="type">int</span> curV = V;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) {</span><br><span class="line"> <span class="keyword">if</span> (curV - arrV[i] >= <span class="number">0</span> && dp[i][curV] == dp[i + <span class="number">1</span>][curV - arrV[i]] + arrW[i]) {</span><br><span class="line"> cout << i << <span class="string">" "</span>;</span><br><span class="line"> curV -= arrV[i];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="求方案总数">求方案总数</h2><p>对于一个给定了背包容量、物品费用、物品间相互关系(分组、依赖等)的背包问题,除了再给定每个物品的价值后求可得到的最大价值外,还可以得到装满背包或将背包装至某一指定容量的方案总数。</p><p>对于这类改变问法的问题,一般只需将状态转移方程中的<code>max</code>改成<code>sum</code>即可。</p><p>例如若每件物品均是完全背包中的物品,转移方程即为<code>f[i][v] = sum{f[i − 1][v], f[i][v − c[i]]}</code>。初始条件<code>f[0][0]=1</code>。</p><p>事实上,这样做可行的原因在于状态转移方程已经考察了所有可能的背包组成方案。</p><h2 id="最优方案的总数">最优方案的总数</h2><p>这里的最优方案是指物品总价值最大的方案。以01背包为例。</p><p>结合求最大总价值和方案总数两个问题的思路,最优方案的总数可以这样求:<code>f[i][v]</code>意义同前述,<code>g[i][v]</code>表示这个子问题的最优方案的总数,则在求<code>f[i][v]</code>的同时求<code>g[i][v]</code>的伪代码如下:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span> <span class="keyword">for</span> i ← <span class="number">1</span> to N</span><br><span class="line"><span class="number">2</span> <span class="keyword">do</span> <span class="keyword">for</span> v ← <span class="number">0</span> to V</span><br><span class="line"><span class="number">3</span> <span class="keyword">do</span> f[i][v] = max{f[i − <span class="number">1</span>][v], f[i − <span class="number">1</span>][v − c[i]] + w[i]}</span><br><span class="line"><span class="number">4</span> g[i][v] = <span class="number">0</span></span><br><span class="line"><span class="number">5</span> <span class="keyword">if</span> (f[i][v] = f[i − <span class="number">1</span>][v])</span><br><span class="line"><span class="number">6</span> then g[i][v] ← g[i][v] + g[i − <span class="number">1</span>][v]</span><br><span class="line"><span class="number">7</span> <span class="keyword">if</span> f[i][v] = f[i − <span class="number">1</span>][v − c[i]] + w[i]</span><br><span class="line"><span class="number">8</span> then g[i][v] ← g[i][v] + g[i − <span class="number">1</span>][v − c[i]]</span><br></pre></td></tr></table></figure><p>如果你是第一次看到这样的问题,请仔细体会上面的伪代码。</p><h3 id="典型例题-2">典型例题</h3><p><a href="https://www.acwing.com/problem/content/11/">背包问题求方案数</a></p><p>有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。第 i 件物品的体积是 vi,价值是 wi。</p><p>求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。</p><p>输出 最优选法的方案数。注意答案可能很大,请输出答案模 <code>10^9+7</code> 的结果。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">constexpr</span> <span class="type">int</span> p = <span class="number">1e9</span> + <span class="number">7</span>;</span><br><span class="line"><span class="type">int</span> N, V;</span><br><span class="line"><span class="type">int</span> dp[<span class="number">1005</span>][<span class="number">1005</span>];</span><br><span class="line"><span class="type">long</span> <span class="type">long</span> ans[<span class="number">1005</span>][<span class="number">1005</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> cin >> N >> V;</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> <span class="built_in">memset</span>(ans, <span class="number">0</span>, <span class="built_in">sizeof</span>(ans));</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i <= V; i++) ans[<span class="number">0</span>][i] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) {</span><br><span class="line"> <span class="type">int</span> v, w;</span><br><span class="line"> cin >> v >> w;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j <= V; j++) {</span><br><span class="line"> dp[i][j] = dp[i - <span class="number">1</span>][j];</span><br><span class="line"> ans[i][j] = ans[i - <span class="number">1</span>][j];</span><br><span class="line"> <span class="keyword">if</span> (j >= v) {</span><br><span class="line"> <span class="keyword">if</span> (dp[i][j] < dp[i - <span class="number">1</span>][j - v] + w) {</span><br><span class="line"> dp[i][j] = dp[i - <span class="number">1</span>][j - v] + w;</span><br><span class="line"> ans[i][j] = ans[i - <span class="number">1</span>][j - v];</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (dp[i][j] == dp[i - <span class="number">1</span>][j - v] + w) {</span><br><span class="line"> ans[i][j] = (ans[i][j] + ans[i - <span class="number">1</span>][j - v]) % p;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << ans[N][V] << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="求次优解、第K优解">求次优解、第K优解</h2><p>对于求次优解、第K优解类的问题,如果相应的最优解问题能写出状态转移方程、用动态规划解决,那么求次优解往往可以相同的复杂度解决,第K优解则比求最优解的复杂度上多一个系数<code>K</code>。</p><p>其基本思想是将每个状态都表示成有序队列,将状态转移方程中的<code>max/min</code>转化成有序队列的合并。这里仍然以01背包为例讲解一下。</p><p>首先看01背包求最优解的状态转移方程:<code>f[i][v] = max{f[i − 1][v], f[i − 1][v − c[i]] + w[i]}</code>。</p><p>如果要求第K优解,那么状态<code>f[i][v]</code>就应该是一个大小为K的数组<code>f[i][v][1..K]</code>。其中<code>f[i][v][k]</code>表示前i个物品、背包大小为v时,第k优解的值。“<code>f[i][v]</code>是一个大小为K的数组”这一句,熟悉C语言的同学可能比较好理解,或者也可以简单地理解为在原来的方程中加了一维。</p><p>显然<code>f[i][v][1..K]</code>这K个数是由大到小排列的,所以我们把它认为是一个有序队列。</p><p>然后原方程就可以解释为:<code>f[i][v]</code>这个有序队列是由<code>f[i-1][v]</code>和<code>f[i-1][v-c[i]]+w[i]</code>这两个有序队列合并得到的。</p><p>有序队列<code>f[i-1][v]</code>即<code>f[i-1][v][1..K]</code>,<code>f[i-1][v-c[i]]+w[i]</code>则理解为在<code>f[i-1][v-c[i]][1..K]</code>的每个数上加上<code>w[i]</code>后得到的有序队列。合并这两个有序队列并将结果的前K项储存到<code>f[i][v][1..K]</code>中的复杂度是<code>O(K)</code>。最后的答案是<code>f[N][V][K]</code>。总的复杂度是<code>Θ(VNK)</code>。</p><p>为什么这个方法正确呢?实际上,一个正确的状态转移方程的求解过程遍历了所有可用的策略,也就覆盖了问题的所有方案。只不过由于是求最优解,所以其它在任何一个策略上达不到最优的方案都被忽略了。如果把每个状态表示成一个大小为K的数组,并在这个数组中有序的保存该状态可取到的前K个最优值。</p><p>那么,对于任两个状态的<code>max</code>运算等价于两个由大到小的有序队列的合并。另外还要注意题目对于“第K优解”的定义,将策略不同但权值相同的两个方案是看作同一个解还是不同的解。如果是前者,则维护有序队列时要保证队列里的数没有重复的。</p><h3 id="典型例题-3">典型例题</h3><p><a href="https://vjudge.net/problem/HDU-2639">Bone Collector II</a></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> arrV[<span class="number">105</span>];</span><br><span class="line"><span class="type">int</span> arrW[<span class="number">105</span>];</span><br><span class="line"><span class="type">int</span> tempA[<span class="number">35</span>];</span><br><span class="line"><span class="type">int</span> tempB[<span class="number">35</span>];</span><br><span class="line"><span class="type">int</span> dp[<span class="number">1005</span>][<span class="number">35</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">int</span> t;</span><br><span class="line"> cin >> t;</span><br><span class="line"> <span class="keyword">while</span> (t--) {</span><br><span class="line"> <span class="type">int</span> N, V, K;</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="built_in">sizeof</span>(dp));</span><br><span class="line"> cin >> N >> V >> K;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++)</span><br><span class="line"> cin >> arrW[i];</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++)</span><br><span class="line"> cin >> arrV[i];</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= N; i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = V; j >= arrV[i]; j--) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">1</span>; k <= K; k++) {</span><br><span class="line"> tempA[k] = dp[j - arrV[i]][k] + arrW[i];</span><br><span class="line"> tempB[k] = dp[j][k];</span><br><span class="line"> <span class="keyword">if</span> (k == K) tempA[k + <span class="number">1</span>] = <span class="number">-1</span>, tempB[k + <span class="number">1</span>] = <span class="number">-1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="type">int</span> x, y, z;</span><br><span class="line"> x = y = z = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">while</span> (z <= K && (tempA[x] != <span class="number">-1</span> || tempB[y] != <span class="number">-1</span>)) {</span><br><span class="line"> <span class="keyword">if</span> (tempA[x] > tempB[y])</span><br><span class="line"> dp[j][z] = tempA[x++];</span><br><span class="line"> <span class="keyword">else</span> dp[j][z] = tempB[y++];</span><br><span class="line"> <span class="keyword">if</span> (dp[j][z] != dp[j][z - <span class="number">1</span>]) z++;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << dp[V][K] << endl;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h1>01背包</h1>
<p><a href="https://www.acwing.com/problem/content/2/">01背包问题</a></p>
<p>有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。</p>
<p>第 i 件物品的体积是 vi</summary>
<category term="算法" scheme="https://creating001.github.io/categories/%E7%AE%97%E6%B3%95/"/>
<category term="动态规划" scheme="https://creating001.github.io/categories/%E7%AE%97%E6%B3%95/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="动态规划" scheme="https://creating001.github.io/tags/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="背包问题" scheme="https://creating001.github.io/tags/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98/"/>
</entry>
<entry>
<title>动态规划之记忆化搜索</title>
<link href="https://creating001.github.io/2023/05/10/dp-0-memo/"/>
<id>https://creating001.github.io/2023/05/10/dp-0-memo/</id>
<published>2023-05-10T15:34:40.000Z</published>
<updated>2023-06-02T11:32:16.295Z</updated>
<content type="html"><![CDATA[<h1>定义</h1><p>记忆化搜索是一种通过记录已经遍历过的状态的信息,从而避免对同一状态重复遍历的搜索实现方式。</p><p>因为记忆化搜索确保了每个状态只访问一次,它也是一种常见的动态规划实现方式。</p><h1>引入</h1><p><a href="https://www.luogu.com.cn/problem/P1048">采药</a></p><h2 id="题目描述">题目描述</h2><p>山洞里有 M 株不同的草药,采每一株都需要一些时间 t_i,每一株也有它自身的价值 v_i。给你一段时间 T,在这段时间里,你可以采到一些草药。让采到的草药的总价值最大。</p><h2 id="暴力DFS做法">暴力DFS做法</h2><p>很容易实现这样一个朴素的搜索做法:在搜索时记录下当前准备选第几个物品、剩余的时间是多少、已经获得的价值是多少这三个参数,然后枚举当前物品是否被选,转移到相应的状态。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">node</span> {</span><br><span class="line"> <span class="type">int</span> time;</span><br><span class="line"> <span class="type">int</span> value;</span><br><span class="line"> <span class="built_in">node</span>() = <span class="keyword">default</span>;</span><br><span class="line"> <span class="built_in">node</span>(<span class="type">int</span> time, <span class="type">int</span> value) : <span class="built_in">time</span>(time), <span class="built_in">value</span>(value) {}</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">node arr[<span class="number">105</span>];</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> T, M;</span><br><span class="line"><span class="type">int</span> ans = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> index, <span class="type">int</span> sumTime, <span class="type">int</span> sumValue)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (sumTime > T) <span class="keyword">return</span>;</span><br><span class="line"> <span class="keyword">if</span> (index == M) {</span><br><span class="line"> ans = <span class="built_in">max</span>(ans, sumValue);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">dfs</span>(index + <span class="number">1</span>, sumTime + arr[index].time, sumValue + arr[index].value);</span><br><span class="line"> <span class="built_in">dfs</span>(index + <span class="number">1</span>, sumTime, sumValue);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> cin >> T >> M;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < M; i++) {</span><br><span class="line"> cin >> arr[i].time;</span><br><span class="line"> cin >> arr[i].value;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">dfs</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"> cout << ans << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这种做法的时间复杂度是指数级别的,并不能通过本题。</p><h2 id="优化">优化</h2><p>上面的做法为什么效率低下呢?因为同一个状态会被访问多次。</p><p>如果我们每查询完一个状态后将该状态的信息存储下来,再次需要访问这个状态就可以直接使用之前计算得到的信息,从而避免重复计算。这充分利用了动态规划中很多问题具有大量重叠子问题的特点,属于用空间换时间的「记忆化」思想。</p><p>具体到本题上,我们在朴素的 DFS 的基础上,增加一个数组 mem 来记录每个 dfs(pos,tleft) 的返回值。刚开始把 mem 中每个值都设成 -1(代表没求解过)。每次需要访问一个状态时,如果相应状态的值在 mem 中为 -1,则递归访问该状态。否则我们直接使用 mem 中已经存储过的值即可。</p><p>通过这样的处理,我们确保了每个状态只会被访问一次,因此该算法的的时间复杂度为 O™。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">node</span> {</span><br><span class="line"> <span class="type">int</span> time;</span><br><span class="line"> <span class="type">int</span> val;</span><br><span class="line"> <span class="built_in">node</span>() = <span class="keyword">default</span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">node arr[<span class="number">105</span>];</span><br><span class="line"><span class="type">int</span> memo[<span class="number">105</span>][<span class="number">1005</span>];</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> T, M;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> index, <span class="type">int</span> sumTime)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (memo[index][sumTime] != <span class="number">-1</span>) <span class="keyword">return</span> memo[index][sumTime];</span><br><span class="line"> <span class="keyword">if</span> (index == M) <span class="keyword">return</span> memo[index][sumTime] = <span class="number">0</span>;</span><br><span class="line"> <span class="type">int</span> dfs1;</span><br><span class="line"> <span class="type">int</span> dfs2 = INT_MIN;</span><br><span class="line"> dfs1 = <span class="built_in">dfs</span>(index + <span class="number">1</span>, sumTime);</span><br><span class="line"> <span class="keyword">if</span> (sumTime + arr[index].time <= T)</span><br><span class="line"> dfs2 = <span class="built_in">dfs</span>(index + <span class="number">1</span>, sumTime + arr[index].time) + arr[index].val;</span><br><span class="line"> <span class="keyword">return</span> memo[index][sumTime] = <span class="built_in">max</span>(dfs1, dfs2);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> cin >> T >> M;</span><br><span class="line"> <span class="built_in">memset</span>(memo, <span class="number">-1</span>, <span class="built_in">sizeof</span>(memo));</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < M; i++) {</span><br><span class="line"> cin >> arr[i].time;</span><br><span class="line"> cin >> arr[i].val;</span><br><span class="line"> }</span><br><span class="line"> cout << <span class="built_in">dfs</span>(<span class="number">0</span>, <span class="number">0</span>) << endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1>与递推的联系与区别</h1><p>在求解动态规划的问题时,记忆化搜索与递推的代码,在形式上是高度类似的。这是由于它们使用了相同的状态表示方式和类似的状态转移。也正因为如此,一般来说两种实现的时间复杂度是一样的。</p><p>下面给出的是递推实现的代码(为了方便对比,没有添加滚动数组优化),通过对比可以发现二者在形式上的类似性。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">node</span> {</span><br><span class="line"> <span class="type">int</span> time;</span><br><span class="line"> <span class="type">int</span> val;</span><br><span class="line"> <span class="built_in">node</span>() = <span class="keyword">default</span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">node arr[<span class="number">105</span>];</span><br><span class="line"><span class="type">int</span> dp[<span class="number">105</span>][<span class="number">1005</span>];</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> T, M;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> cin >> T >> M;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i <= T; i++) dp[<span class="number">0</span>][i] = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= M; i++) {</span><br><span class="line"> cin >> arr[i].time;</span><br><span class="line"> cin >> arr[i].val;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i <= M; i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j <= T; j++) {</span><br><span class="line"> dp[i][j] = dp[i - <span class="number">1</span>][j];</span><br><span class="line"> <span class="keyword">if</span> (j >= arr[i].time)</span><br><span class="line"> dp[i][j] = <span class="built_in">max</span>(dp[i][j], dp[i - <span class="number">1</span>][j - arr[i].time] + arr[i].val);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cout << dp[M][T];</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在求解动态规划的问题时,记忆化搜索和递推,都确保了同一状态至多只被求解一次。而它们实现这一点的方式则略有不同:递推通过设置明确的访问顺序来避免重复访问,记忆化搜索虽然没有明确规定访问顺序,但通过给已经访问过的状态打标记的方式,也达到了同样的目的。</p><p>与递推相比,记忆化搜索因为不用明确规定访问顺序,在实现难度上有时低于递推,且能比较方便地处理边界情况,这是记忆化搜索的一大优势。但与此同时,记忆化搜索难以使用滚动数组等优化,且由于存在递归,运行效率会低于递推。因此应该视题目选择更适合的实现方式。</p><h1>如何写记忆化搜索</h1><h2 id="方法一">方法一</h2><ol><li>把这道题的 dp 状态和方程写出来</li><li>根据它们写出 dfs 函数</li><li>添加记忆化数组</li></ol><p>举例:</p><p><a href="https://leetcode.cn/problems/longest-increasing-subsequence/">最长上升子序列</a></p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>d</mi><msub><mi>p</mi><mi>i</mi></msub><mo>=</mo><mi>max</mi><mo></mo><mo stretchy="false">{</mo><mi>d</mi><msub><mi>p</mi><mi>j</mi></msub><mo>+</mo><mn>1</mn><mo stretchy="false">}</mo><mspace width="1em"/><mo stretchy="false">(</mo><mn>1</mn><mo>≤</mo><mi>j</mi><mo><</mo><mi>i</mi><mo>∧</mo><msub><mi>a</mi><mi>j</mi></msub><mo><</mo><msub><mi>a</mi><mi>i</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">dp_{i} = \max\{dp_{j}+1\}\quad (1 \leq j < i \land a_{j}<a_{i}) </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">d</span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span></span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0361em;vertical-align:-0.2861em;"></span><span class="mop">max</span><span class="mopen">{</span><span class="mord mathnormal">d</span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span></span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">1</span><span class="mclose">}</span><span class="mspace" style="margin-right:1em;"></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.854em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05724em;">j</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"><</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6595em;"></span><span class="mord mathnormal">i</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">∧</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.8252em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span></span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"><</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span></span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> {</span><br><span class="line"> vector<<span class="type">int</span>> memo;</span><br><span class="line"> <span class="function"><span class="type">int</span> <span class="title">dfs</span><span class="params">(vector<<span class="type">int</span>>& nums, <span class="type">int</span> i)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (memo[i] != <span class="number">-1</span>) <span class="keyword">return</span> memo[i];</span><br><span class="line"> <span class="type">int</span> ret = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < i; j++) {</span><br><span class="line"> <span class="keyword">if</span> (nums[i] > nums[j]) ret = <span class="built_in">max</span>(ret, <span class="built_in">dfs</span>(nums, j) + <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> memo[i] = ret;</span><br><span class="line"> }</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">int</span> <span class="title">lengthOfLIS</span><span class="params">(vector<<span class="type">int</span>>& nums)</span> </span>{</span><br><span class="line"> memo = <span class="built_in">vector</span><<span class="type">int</span>>(nums.<span class="built_in">size</span>() - <span class="number">1</span>, <span class="number">-1</span>);</span><br><span class="line"> <span class="type">int</span> ret = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i < nums.<span class="built_in">size</span>(); i++) {</span><br><span class="line"> ret = <span class="built_in">max</span>(ret, <span class="built_in">dfs</span>(nums, i));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h2 id="方法二">方法二</h2><ol><li>写出这道题的暴搜程序(最好是 dfs)</li><li>将这个 dfs 改成「无需外部变量」的 dfs</li><li>添加记忆化数组</li></ol>]]></content>
<summary type="html"><h1>定义</h1>
<p>记忆化搜索是一种通过记录已经遍历过的状态的信息,从而避免对同一状态重复遍历的搜索实现方式。</p>
<p>因为记忆化搜索确保了每个状态只访问一次,它也是一种常见的动态规划实现方式。</p>
<h1>引入</h1>
<p><a href="https:</summary>
<category term="算法" scheme="https://creating001.github.io/categories/%E7%AE%97%E6%B3%95/"/>
<category term="动态规划" scheme="https://creating001.github.io/categories/%E7%AE%97%E6%B3%95/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="动态规划" scheme="https://creating001.github.io/tags/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="记忆化搜索" scheme="https://creating001.github.io/tags/%E8%AE%B0%E5%BF%86%E5%8C%96%E6%90%9C%E7%B4%A2/"/>
</entry>
</feed>