9.6 训练参考
动态规划是算法竞赛的宠儿——几乎所有算法竞赛中都会出现动态规划的题目。本章虽然也包含一些知识点和理论讲解,但重中之重是那些经典题目(例如,LIS、LCS、最优矩阵链乘、树的重心和TSP等)和例题。本章的例题数量是本书目前为止最多的,难度也是最大的。建议读者先掌握不带星号的例题,然后逐步学习带一个星号的例题和两个星号的例题。有些例题比较复杂,甚至需要反复理解才能掌握。例题列表如表9-2所示。
表9-2 例题列表
| 类别 | 题号 | 题目名称(英文) | 备注 |
| 例题9-1 | UVa1025 | A Spy in the Metro | DAG的动态规划 |
| 例题9-2 | UVa437 | The Tower of Babylon | DAG的动态规划 |
| 例题9-3 | UVa1347 | Tour | 经典问题 |
| 例题9-4 | UVa116 | Unidirectional TSP | 多段图的最短路;字典序最小解 |
| 例题9-5 | UVa12563 | Jin Ge Jin Qu [h]ao | 0-1背包问题 |
| 例题9-6 | UVa11400 | Lighting System Design | 线性结构上的动态规划 |
| 例题9-7 | UVa11584 | Partitioning by Palindromes | 线性结构上的动态规划;优化 |
| 例题9-8 | UVa1625 | Color Length | 类似于LCS的动态规划;指标函数的分解 |
| 例题9-9 | UVa10003 | Cutting Sticks | 类似于最优矩阵链乘的动态规划 |
| 例题9-10 | UVa1626 | Brackets Sequence | 递归结构的动态规划 |
| *例题9-11 | UVa1331 | Minimax Triangulation | 类似于最优三角剖分的动态规划 |
| 例题9-12 | UVa12186 | Another Crisis | 树形动态规划 |
| 例题9-13 | UVa1220 | Party at Hali-Bula | 树形动态规划;解的唯一性 |
| 例题9-14 | UVa1218 | Perfect Service | 树形动态规划;状态转移方程的优化 |
| 例题9-15 | UVa10817 | Headmaster's Headache | 集合的动态规划;位运算 |
| 例题9-16 | UVa1252 | Twenty Questions | 集合的动态规划;时间优化 |
| *例题9-17 | UVa1412 | Fund Management | 复杂状态的动态规划;和指标函数值有关的状态转移 |
| 例题9-18 | UVa10618 | Tango Tango Insurrection | 多阶段决策问题 |
| 例题9-19 | UVa1627 | Team them up! | 图论模型;0-1背包 |
| 例题9-20 | UVa10934 | Dropping water balloons | 经典问题 |
| 例题9-21 | UVa1336 | Fixing the Great Wall | 动态规划中“未来费用”的计算 |
| 例题9-22 | UVa12105 | Bigger is Better | 用动态规划辅助其他算法 |
| *例题9-23 | UVa1204 | Fun Game | 字符串集合的动态规划 |
| *例题9-24 | UVa12099 | Bookcase | 类似0-1背包问题的动态规划;状态优化 |
| *例题9-25 | UVa12170 | Easy Climb | 最优解的特征分析;用单调队列优化动态规划 |
| *例题9-26 | UVa1380 | A Scheduling Problem | 树的动态规划(复杂) |
| **例题9-27 | UVa10559 | Blocks | 给状态增加维度 |
| *例题9-28 | UVa1439 | Exclusive Access 2 | 图论模型;Dilworth定理 |
| **例题9-29 | UVa1228 | Integer Transmission | 深入分析问题 |
| **例题9-30 | UVa1375 | The Best Name for Your Baby | 上下文无关文法;有“环”的动态规划 |
| **例题9-31 | UVa1628 | Pizza Delivery | 深入分析问题 |
下面是一些形形色色的动态规划问题,难度各异。建议读者阅读所有题目,然后认真思考每一道题。对于能写出状态转移方程的题目,尽量编程提交。
习题9-1 最长的滑雪路径(Longest Run on a Snowboard, UVa 10285)
在一个R*C(R,C≤100)的整数矩阵上找一条高度严格递减的最长路。起点任意,但每次只能沿着上下左右4个方向之一走一格,并且不能走出矩阵外。如图9-29所示,最长路就是按照高度25, 24, 23,…, 2, 1这样走,长度为25。矩阵中的数均为0~100。
图9-29 最长路径示例
习题9-2 免费糖果(Free Candies, UVa 10118)
桌上有4堆糖果,每堆有N(N≤40)颗。佳佳有一个最多可以装5颗糖的小篮子。他每次选择一堆糖果,把最顶上的一颗拿到篮子里。如果篮子里有两颗颜色相同的糖果,佳佳就把它们从篮子里拿出来放到自己的口袋里。如果篮子满了而里面又没有相同颜色的糖果,游戏结束,口袋里的糖果就归他了。当然,如果佳佳足够聪明,他有可能把堆里的所有糖果都拿走。为了拿到尽量多的糖果,佳佳该怎么做呢?
习题9-3 切蛋糕(Cake Slicing, ACM/ICPC Nanjing 2007, UVa1629)
有一个n行m列(1≤n,m≤20)的网格蛋糕上有一些樱桃。每次可以用一刀沿着网格线把蛋糕切成两块,并且只能够直切不能拐弯。要求最后每一块蛋糕上恰好有一个樱桃,且切割线总长度最小。如图9-30所示是一种切割方法。
图9-30 蛋糕切法示例
习题9-4 串折叠(Folding, ACM/ICPC NEERC 2002, UVa1630)
给出一个由大写字母组成的长度为n(1≤n≤100)的串,“折叠”成一个尽量短的串。例如,AAAAAAAAAABABABCCD折叠成9(A)3(AB)CCD。折叠是可以嵌套的,例如,NEERCYESYESYESNEERCYESYESYES可以折叠成2(NEERC3(YES))。多解时可以输出任意解。
习题9-5 邮票和信封(Stamps and Envelope Size, ACM/ICPC World Finals 1995, UVa242)
假定一张信封最多贴5张邮票,如果只能贴1分和3分的邮票,可以组成面值1~13以及15,但不能组成面值14。我们说:对于邮票组合{1,3}以及数量上限S=5,最大连续邮资为13。1~13和15的组成方法如表9-3所示。
表9-3 1~3和15的组成方法

输入S(S≤10)和若干邮票组合(邮票面值不超过100),选出最大连续邮资最大的一个组合。如果有多个并列,邮票组合中邮票的张数应最多。如果还有并列,邮票从大到小排序后字典序应最大。
习题9-6 电子人的基因(Cyborg Genes, UVa 10723)
输入两个A~Z组成的字符串(长度均不超过30),找一个最短的串,使得输入的两个串均是它的子序列(不一定连续出现)。你的程序还应统计长度最短的串的个数。例如,ABAAXGF和AABXFGA的最优解之一为AABAAXGFGA,一共有9个解。
习题9-7 密码锁(Locker, Tianjin 2012, UVa1631)
有一个n(n≤1000)位密码锁,每位都是0~9,可以循环旋转。每次可以让1~3个相邻数字同时往上或者往下转一格。例如,567890->567901(最后3位向上转)。输入初始状态和终止状态(长度不超过1000),问最少要转几次。例如,111111到222222至少转2次,由896521到183995则要转12次。
习题9-8 阿里巴巴(Alibaba, ACM/ICPC SEERC 2004, UVa1632)
直线上有n(n≤10000)个点,其中第i个点的坐标是xi,且它会在di秒之后消失。Alibaba可以从任意位置出发,求访问完所有点的最短时间。无解输出No solution。
习题9-9 仓库守卫(Storage Keepers, UVa10163)
你有n(n≤100)个相同的仓库。有m(m≤30)个人应聘守卫,第i个应聘者的能力值为Pi(1≤Pi≤1000)。每个仓库只能有一个守卫,但一个守卫可以看守多个仓库。如果应聘者i看守k个仓库,则每个仓库的安全系数为Pi/K的整数部分。没人看守的仓库安全系数为0。
你的任务是招聘一些守卫,使得所有仓库的最小安全系数最大,在此前提下守卫的能力值总和(这个值等于你所需支付的工资总和)应最小。
习题9-10 照亮体育馆(Barisal Stadium, UVa10641)
输入一个凸n(3≤n≤30)边形体育馆和多边形外的m(1≤m≤1000)个点光源,每个点光源都有一个费用值。选择一组点光源,照亮整个多边形,使得费用值总和尽量小。如图9-31所示,多边形ABCDEF可以被两组光源{1,2,3}和{4,5,6}照亮。光源的费用决定了哪组解更优。

图9-31 被点光源照亮的多边形
习题9-11 禁止的回文子串(Dyslexic Gollum, ACM/ICPC Amritapuri 2012, UVa1633)
输入正整数n和k(1≤n≤400,1≤k≤10),求长度为n的01串中有多少个不含长度至少为k的回文连续子串。例如,n=k=3时只有4个串满足条件:001, 011, 100, 110。
习题9-12 保卫Zonk(Protecting Zonk, ACM/ICPC Dhaka 2006, UVa12093)
给定一个有n(n≤10000)个结点的无根树。有两种装置A和B,每种都有无限多个。
求覆盖所有边的最小花费。
习题9-13 叠盘子(Stacking Plates, ACM/ICPC World Finals 2012, UVa1289)
有n(1≤n≤50)堆盘子,第i堆盘子有hi个盘子(1≤hi≤50),从上到下直径不减。所有盘子的直径均不超过10000。有如下两种操作。
你的任务是用最少的操作把所有盘子叠成一堆。
习题9-14 圆和多边形(Telescope, ACM/ICPC Tsukuba 2000, UVa1543)
给你一个圆和圆周上的n(3≤n≤40)个不同点。请选择其中的m(3≤m≤n)个,按照在圆周上的顺序连成一个m边形,使得它的面积最大。例如,在图9-32中,右上方的多边形最大。
图9-32 圆和多边形问题示意图
习题9-15 学习向量(Learning Vector, ACM/ICPC Dhaka 2012, UVa12589)
输入n个向量(x,y)(0≤x,y≤50),要求选出k个,从(0,0)开始画,使得画出来的折线与x轴围成的图形面积最大。例如,4个向量是(3,5), (0,2), (2,2), (3,0),可以依次画(2,2), (3,0), (3,5),围成的面积是21.5,如图9-33所示。输出最大面积的两倍。1≤k≤n≤50。
习题9-16 野餐(The Picnic, ACM/ICPC NWERC 2002, UVa1634)
输入m(m≤100)个点,选出其中若干个点,以这些点为顶点组成一个面积最大的凸多边形,使得内部没有输入点(边界上可以有)。输入点的坐标各不相同,且至少有3个点不共线,如图9-34所示。
| ![]() |
| 图9-33 向量所围面积 | 图9-34 输入点 |
习题9-17 佳佳的筷子(Chopsticks, UVa 10271)
中国人吃饭喜欢用筷子。佳佳与常人不同,他的一套筷子有3只,两根短筷子和一只比较长的(一般用来穿香肠之类的食物)。两只较短的筷子的长度应该尽可能接近,但是最长那只的长度无须考虑。如果一套筷子的长度分别是A,B,C(A≤B≤C),则用(A-B)2的值表示这套筷子的质量,这个值越小,这套筷子的质量越高。
佳佳请朋友吃饭,并准备为每人准备一套这种特殊的筷子。佳佳有N(N≤1000)只筷子,他希望找到一种办法搭配好K+8套筷子,使得这些筷子的质量值和最小。保证筷子足够,即3K+24≤N。
提示:需要证明一个猜想。
习题9-18 棒球投手(Pitcher Rotation, ACM/ICPC Kaosiung 2006, UVa1379)
你经营着一支棒球队。在接下来的g+10天中会有g(3≤g≤200)场比赛,其中每天最多一场比赛。你已经分析出你的n(5≤n≤100)个投手中每个人对阵所有m(3≤m≤30)个对手的胜率(一个n*m矩阵),要求给出作战计划(即每天使用哪个投手),使得总获胜场数的期望值最大。注意,一个投手在上场一次后至少要休息4天。
提示:如果直接记录前4天中每天上场的投手编号1~n,时间和空间都无法承受。
习题9-19 花环(Garlands, ACM/ICPC CERC 2009, UVa1443)
你的任务是用n(n≤40000)条等长细绳组成一个花环。每条细绳上都有一颗珍珠,重量为wi(1≤wi≤10000)。花环应由m(2≤m≤10000)个片段组成,每个片段必须包含连续的偶数条细绳。每个片段的一半称为“半段”(两个半段包含相同数量的细绳),每个“半段”最多能有d(1≤d≤10000)条细绳。你的任务是让最重的半段尽量轻。如图9-35所示,12条细绳的最优解是如下的3个片段,最重的半段的重量为6(左数第1, 4, 6个半段)。
图9-35 12条细绳的最优解
习题9-20 山路(Mountain Road, NWERC 2009, UVa12222)
有一条狭窄的山路只有一个车道,因此不能有两辆相反方向的车同时驶入。另外,为了确保安全,对于山路上的任意一点,相邻的两辆同向行驶的车通过它的时间间隔不能少于10秒。给定n(1≤n≤200)辆车的行驶方向、到达时刻(对于往右开的车来说是到达山路左端点的时刻,而对于往左开的车来说是指到达右端点的时刻),以及行驶完山路的最短时间(为了保证安全,实际行驶时间可以高于这个值),输出最后一辆车离开山路的最早时刻。输入保证任意两辆车的到达时刻均不相同。
提示:本题的主算法并不难,但是实现细节需要仔细推敲。
习题9-21 周期(Period, ACM/ICPC Seoul 2006, UVa1371)
两个串的编辑距离为进行的修改、删除和插入操作次数的最小值(每次一个字符)。如图9-36所示,A=abcdefg和B=ahcefig的编辑距离为3。
图9-36 编辑距离
如果x可以分成若干部分,使得每部分和y的编辑距离都不超过k,则y是x的k-近似周期。例如,x=abcdabcabb,y=abc,x可以分解为abcd+abc+abb,3部分和y的编辑距离分别为1, 0, 1,因此y是x的1-近似周期。
输入由小写字母组成的x和y,求最小的k使得y是x的k-近似周期。|y|≤50,|x|≤5000。
提示:直接想出的动态规划算法很可能太慢,要想办法降低时间复杂度。
习题9-22 俄罗斯套娃(Matryoshka, ACM/ICPC World Finals 2013, UVa 1579)
桌上有n(n≤500)个套娃排成一行,你的任务是把它们套成若干个套娃组,使得每个套娃组内的套娃编号恰好是从1开始的连续编号。操作规则如下:
执行合并操作的前后,所有套娃都是关闭的。为了合并两个套娃组,你需要交替地把一些套娃打开、重新套起来、关闭。例如,为了合并[1, 2, 6]和[4],需要打开套娃6和4;为了合并[1, 2, 5]和[3, 4],需要打开套娃5, 4, 3(只有先打开4才能打开3)。要求打开/关闭的总次数最少。无解输出impossible。例如,“1 2 3 2 4 1 3”需要打开7次,如表9-4所示。
表9-4 “1 2 3 2 4 1 3”需打开7次
| 操作前 | 操作后 | 打开的套娃 |
| 1 2 3 2 4 1 3 | [1 2] 3 2 4 1 3 | 2 |
| [1 2] 3 2 4 1 3 | [1 2 3] 2 4 1 3 | 3 |
| [1 2 3] 2 4 1 3 | [1 2 3] [2 4] 1 3 | 4 |
| [1 2 3] [2 4] 1 3 | [1 2 3] [2 4 1] 3 | 4, 2 |
| [1 2 3] [2 4 1] 3 | [1 2 3] [2 4 1 3] | 4, 3 |
习题9-23 优化最大值电路(Minimizing Maximizer, ACM/ICPC CERC 2003, UVa1322)
所谓Maximizer,就是一个n输入1输出的硬件电路,它可以用若干个串行Sorter来实现,其中每个Sorter(i,j)表示把第i~j个输入从小到大排序。最后一个Sorter的第n个输出就是整个Maximizer的输出。输入一个由m个Sorter组成的Maximizer,保留尽量少的Sorter(顺序不变),使得Maximizer仍能正常工作。n≤50000,m≤500000。
————————————————————
(1) 注意这个函数的工作方式并不像它表面显示的那样——如果把-1改成-2,并不是在把所有d值都初始化为-2!请只用0和-1作为“批量赋值”的参数。
(2) 输出的最后会有一个多余空格,并且没有回车符。在使用时,应在主程序调用print_ans后加一个回车符。如果比赛明确规定行末不允许有多余空格,则可以像前面介绍的那样加一个变量first来帮助判断。
(3) 如果状态比较复杂,推荐用STL中的map而不是普通数组保存状态值。这样,判断状态S是否算过只需用if(d.count(S))即可。
(4) 第二个人走到i+1时本应转移到d(i,i+1),但是根据此处规定,必须写成d(i+1,i)。
(5) 还有《劲歌金曲2》和《劲歌金曲3》,但本题不予考虑。
(6) 显然大多数歌的长度都大于3分钟,但是KTV可以“切歌”,因此这里的“长度”实际上是指“想唱的时间长度”。
(7) 判断回文也可以用动态规划,读者不妨一试。
(8) 虽然思路很清晰,但具体实现还需要斟酌,建议读者独立完成。
(9) 如何判断i-j是否为多边形的对角线?限于篇幅,本书没有对计算几何进行专门讨论,请读者参考《算法竞赛入门经典——训练指南》的几何部分。
(10) 所谓NPC,即NP-完全问题(NP-Complete Problem),是指一类目前还没有找到多项式算法的问题。它的确切定义超出了本书的范围。
(11) 完整实现见代码仓库。
(12) 其实还有一个更简单的做法,既不需要高精度,也不需要“反着想”,参见代码仓库。
(13) 本题的解法看上去比较常规,但是在NWERC这样较高水平的比赛中,却没有队伍做出来。
(14) 证明思路是从定向方案构造分层图。先把所有路径的起点作为第0层。
(15) 准确地说这不是动态规划,而是组合数学中的递推,因为本题不是最优化问题,而是计数问题。不过解决两个问题的思路是相同的,所以很多人把组合数学中的递推也算作动态规划。
(16) 本题在实现上有一些细节需要注意,建议参考代码仓库。