递推法/动态规划

目录

  • 概述
  • 背包类型动态规划

递推法常常用来解决

  • 计数问题
  • 组合最优化问题

动态规划dynamic programming,DP

一种解题技术和算法设计方法。

  • 狭义:用子问题递推的方法解决组合最优化问题。
  • 广义:和递推法是同义词。

递推三要素

  • 子问题(状态)
  • 递推式(状态转移)
  • 边界条件(边界状态)

⚠️ 这不是递推法/DP 的第一课,我们不会从头讲起。

有 个东西排成一行,要从中选一些,可以一个也不选。相邻的两个东西不能都选。有多少种方法?

解法:递推

子问题

从前 个东西里选一些,相邻两个不能都选,有多少种方法?
这里,。
把答案记作 。

目标:。

边界条件:,。

递推式:,。

https://bs.daimayuan.top/p/112

例题:构造序列

给你一个长为 的整数序列 。序列 初始为空,我们要通过下列两种操作把 变成 。

  • 批量添加:选定任意非负整数 ,在 末尾添加 个 ,花费 个金币。
  • 连续添加:选定任意非负整数 ,在 末尾添加 个从 开始的连续整数,(即 ),花费 个金币。

求把 变成 最少要花多少个金币。

限制
  • 。

递推

子问题

把 变成 最少要花多少金币。
这里 。把答案记作 。

目标:

边界条件:

怎么找递推式?

递推式

考虑把 变成 的最后一步。
有「批量添加」和「连续添加」两个选择,但是添加多少项却有 个选择。
注意到

  • 操作的费用和添加的项数无关
  • ()

所以添加的项数越大越好。

阿弥陀签

又称鬼脚图、爬梯游戏,是一种游戏,也是一种简易决策方法,常被拿作抽签或决定分配组合。

先画几条平行的竖线,再画一些横线,每条横线连接相邻两条竖线。两条横线不能相交。

玩法:

  1. 把画的横线盖住,选一条竖线。
  2. 揭开盖子,从所选竖线的上端往下走,遇到横线就走过去,直到走到某条竖线的下端。

例题:阿弥陀签的数量

有 条长度是 厘米的竖线,要画一些横线做成一个阿弥陀签。每条横线的两端点到竖线上端的距离必须是 ,,……,或 厘米。有多少种画法满足:

  • 从第一条竖线的上端出发最后会走到第 条竖线的下端。

输出答案模 。

限制
样例

位置,一步

用一对整数 表示位置:在第 条竖线上,距离竖线的上端 厘米。
,。

初始位置 ,目标位置 。

设当前位置是 ,称下述动作为一步

  • 向下移动 1 厘米,到 。若 处有横线,就沿着横线走过去。

递推

各行相互独立。从上到下考虑每一行。

子问题

:对前 行,有多少种画法使得

  • 从 经过 步到达 。

边界条件:。

转移:

https://bs.daimayuan.top/p/159

例题:勇者斗恶龙

apiadu 正在与一头巨龙进行决战。这场战斗将持续 个回合。
在第 个回合,apiadu 可以从以下两种行动中选择一个:

  • 提高攻击力:apiadu 花费整个回合来念咒,这会使他的攻击力永久提升 点。
  • 攻击:apiadu 释放一道冲击波攻击巨龙。冲击波的伤害值为他当前的攻击力 + 。

apiadu 的初始攻击力为 。他的目标是合理安排每个回合的行动,使得在 回合结束后,对巨龙造成的总伤害最高。

请你帮他计算能造成的最多伤害。


DP 的两种实现方式

  • 后向DP(填表法,pull DP)
  • 前向DP(刷表法,push DP)

在各种结构上使用动态规划

  • 序列
  • 网格
  • 树
  • ……

序列上的动态规划

序列有两种子结构,前缀(用一个参数描述)和区间(用两个参数描述)。

  • 用前缀定义子问题
  • 用区间定义子问题

背包类型动态规划

例题:Knapsack 2

有 个物品。编号 。第 个物品的重量是 ,价值是 。

太郎要从 个物品中选一些放进背包带回家。背包的容量是 ,这意味着所选物品的重量之和不能超过 。

求所选物品的价值之和的最大值。

限制
  • 以上值都是整数。

虽然这是个古老的问题,也许有人不知道,这里再提一下。

子问题

从前 个物品中选一些,所选物品的总重量不超过 ,所选物品的总价值的最大值。

这里,,,子问题有 个,太多了。

注意到 ,,这意味着全部物品的价值之和不超过 。

新的子问题

从前 个物品中选一些,所选物品的总价值等于 ,所选物品的总重量的最小值。

这里,,。

把这个问题的答案记作 ,若无法做到,令 。

边界条件:,,。

递推式:对 ,有

例题:多边形 [CSP-J 2025]

有 根木棍,第 根木棍的长度是 ,要从中选择一些木棍拼成一个多边形,有多少种选法?

两种方案不同当且仅当选择的木棍的下标集合不同,即存在 (),使得其中一种方案选择了第 根木棍,但另一种方案未选择。

根木棍,长度为 ,能拼成一个多边形当且仅当 且所有木棍的长度之和大于所有木棍的长度的最大值的两倍,即 。

限制

解法

所有木棍的长度之和大于所有木棍的长度的最大值的两倍,即

  • 除了最长木棍之外的木棍的总长度大于最长木棍的长度。

把木棍按长度从小到大排序,以下假设有 。

设所选的编号最大的那一根木棍,也就是最长的那一根,的编号是 ,问题变成

  • 从前 根木棍中选一些,所选木棍的长度之和不小于 ,有多少种选法。

考虑 DP。

子问题

:从前 根木棍中选一些,所选木棍的长度之和不小于 ,有多少种选法。

,

边界条件

递推式

最终答案
。

例题:盒饭 2

高桥为 Snuke 准备了 个盒饭,从 到 编号。盒饭 里有 个鱼丸和 个肉丸。

高桥可以把这些盒饭任意排列。Snuke 会按高桥排好的顺序吃这些盒饭,吃完一个盒饭之后,如果 Snuke 吃的鱼丸的总数大于 或者吃的肉丸的总数大于 ,Snuke 就不能再吃了。

高桥想让 Snuke 吃掉的盒饭的数量尽可能多。求 Snuke 最多能吃掉多少个盒饭。

限制

分析

Snuke 至少吃一个盒饭。
在吃最后一个盒饭之前,他吃的鱼丸不超过 个,肉丸不超过 个。

假设在吃的鱼丸不超过 个且肉丸不超过 个的前提下 Snuke 最多能吃 个盒饭,那么实际上他最多能吃 个盒饭。

我们可以用背包类型的动态规划来求 。

子问题

:从前 个盒饭中选 个吃掉,吃和鱼丸总数不超过 ,吃的肉丸总数的最小值。

就是满足 的最大的整数 。

例题:分成三队

有 个人分成了三队。人编号 ,队编号 。现在,人 属于队 。

每人有一个强度值,人 的强度是 。一个队的强度定义为其成员的强度之和。判断能否通过改变某些人所属的队使得三个队的强度相同。若可能,输出最少要几个人换队。否则输出 。

限制
  • 上述值都是整数。

分析

必要条件 1: 整除 。

令 。注意到 。

必要条件 2:。

子问题

把前 个人分到三个队,1 队的强度是 ,2 队的强度是 ,最少有几个人换队?

把它的答案记作 。若无法做到,令 。
这里 ,。

目标:。

边界条件:,, 或 。

递推式:

例题:Diversity

商店里有 个商品在售,第 个商品的价格是 元,效用是 ,颜色是 。

你要从这些商品中买一些(可以一个都不买)。所买物品的价格之和不得超过 元。

你的满意度是 ,其中 是所买物品的效用之和, 是所买物品的不同颜色的数量, 是给定的常数。

求最大满意度。

限制
  • 上述值都是整数。

思路一

把 个商品按颜色排列,颜色相同的排一起。

子问题

:从前 个商品里选一些,所选商品的总价格不超过 ,满意度的最大值。

考虑两种情况:

  1. 不买商品 ,归结为 。
  2. 买商品 。设商品 的颜色是 。分两种情况:
    1. 商品 是买的第一个颜色为 的商品。设上一种颜色的最后一个商品是第 个,归结为 。
    2. 商品 不是买的第一个颜色为 的商品。归结为 。

思路二

把物品按颜色分组。一次处理一批同色的物品。

子问题

从颜色是 的物品中选一些,所选物品的总价不超过 ,满意度的最大值?

把答案记作 。这里,,。

目标:。

边界条件:,。

递推式:
对每种颜色 ,

  1. 置 ,。
  2. 对每个颜色是 的物品 ,按 的顺序,置

例题:分组

给你 个整数 。要把它们分成若干组,每一组的极差之和不超过 。求分组方法数,模 。

一组整数的极差是最大值减最小值。

限制
样例
3 2
2 5 3
3

分组方法:

  • (2), (3), (5)
  • (2, 3), (5)
  • (2), (3, 5)

把 个数从大到小排序。

子问题

:对前 个数分组,尚未分好的组(最小值尚未出现)有 个,且满足

  • 已分好的组的极差之和 + 未分好的组的最大值之和 ≤

有多少种方法?

注意到

目标:。

边界条件:(),()。

状态转移

例题:两场考试

个人,从 到 编号,参加了国家队选拔考试。
考试有两场。第 个人第一场考试的排名是 ,第二场考试的排名是 。每场考试都没有同分的, 都是 的排列。

要选 个人组国家队,选人规则是:

  • 不能有两人 满足 且 , 入选了而 没入选。

求选人方案数,模 。

限制

思路

把 个人按照第一场考试的排名从小到大(或者说从高到低)排序,按这个顺序写出每个人第二场考试的排名,把这个序列称为 。不难看出 是 的一个排列。

例如,, 那么 。

问题变成

  • 从序列 中选 项,满足:若选了 那么 左边比 小的项都选了。或者说若没选 那么 右边比 大的项都没有选。有多少种选法?

考虑 DP。

子问题

:从 中选 个数,没选数的数中最小的是 ,有多少种选法。

从 往后转移:

  • 若 ,那么 不能选,转移到 。
  • 若 ,那么 可选可不选。若选 ,转移到 ,否则转移到 。

例题:Candy

给定整数序列 ,整数 和整数 。我们称交换 里相邻的两项为一次操作。

要使 的前 项之和不小于 ,至少要进行多少次操作?

限制

  • ()

思路

设想我们已经选定了要放到前 个位置的那 个数,它们,在最初的序列 里,是 ()。

要把 这 个数移到前 个位置,最少要操作多少次?

应该把 移动到第 位,把 移动到第 位,……,把 移动到第 位。
一共要操作 次。

子问题

:从 中选 个数,把它们移动到前 位需要操作 次,所选的数之和的最大值。

,,。

答案:满足 的最小的 。

边界条件:

()
( 或 )

递推式:

例题:纪念品 ⭐

已知未来 天 种纪念品每天的价格,第 天第 种纪念品的价格是 。
每天,小明可以进行以下两种交易任意多次:

  • 以当日价格买一个纪念品;
  • 以当日价格卖出持有的一个纪念品。

每天卖出纪念品换回的金币可以立即用于购买纪念品。

小明会在第 天卖出所有纪念品,他现在有 枚金币, 天之后他最多有几枚金币?

限制
  • 任意时刻,小明手上的金币数不超过。

思路

「第一天买一个纪念品,第三天卖」相当于
「第一天买,第二天卖,第二天买,第三天卖」。

所有可能的操作都可变换为

  • 第一天:买一些纪念品。
  • 第二天:把第一天买的纪念品全卖出,再买一些纪念品。
  • 第三天:把第二天买的纪念品全卖出,再买一些纪念品。
  • ……
  • 第 天:把第 天买的纪念品全卖出。

第一天买纪念品的目标应是让所买的纪念品在第二天卖出后收益最大。

  • 解 个完全背包问题。

例题:Emiya 家今天的饭 ⭐

Emiya 掌握 种烹饪方法,且会使用 种主要食材做菜。

Emiya 做的每道菜都将使用恰好一种烹饪方法与恰好一种主要食材。Emiya 会做 道不同的使用烹饪方法 和主要食材 的菜(、)。

Emiya 今天要做 道菜,要满足下列条件

  • 每道菜的烹饪方法互不相同
  • 每种主要食材至多在至多在一半的菜(即 道菜)中被使用

求做菜方案数,模 。



思路

  • 令 。
  • 满足条件一、二的做菜方案的数量为 。
  • 考虑满足条件一、二但不满足条件三的做菜方案的数量。
    • 有主要食材在超过一半的菜中被使用。
    • 在超过一半的菜中被使用的主要食材只能有一种。

对于每一种主要食材 (),计算 使用次数超过一半的做菜方案。

子问题

:从前 个烹饪方法中选一些来做菜,主要食材是 的菜与主要食材不是 的菜数量之差是 的做菜方案的数量。

,。

递推式

边界条件
,()

例题:愿望清单

商店里有 个商品,从 到 编号。商品 的价格是 元。
高桥想要 个商品:商品 ,商品 ,…,商品 。

他重复下述操作直到买齐他想要的商品。

令 为当前尚未卖出去的商品的数量。选择一个整数 满足 ,买剩余物品中编号第 小的那个物品,花的钱是那个物品的价格再加上 元。

求高桥最少要花多少钱。

高桥也可以买他不想要的物品。

限制

关键性质

  • 编号大于 的商品不影响高桥想买的物品的编号的排位,不用考虑。
    不妨置 。
  • 何时买商品 不影响对前 个商品中任何一个的购买。
  • 设最终在前 个商品中买了 个,如果也买了商品 ,那么买它时,它的编号排名可以是 中的任何一个。所以买商品 的花费是 。
子问题

:在前 个商品中买 个,想要的商品的都买了,最少要花多少钱?

这里,,。
状态 要满足

前个物品中必买的物品的数量

才合理。
若状态 不合理,令 。

目标:。

边界条件:()

递推式:对 ,

商品必买商品不必买

例题:Goodstuff Deck Builder(Hard)

yukicoder 3077

你在玩一个卡牌游戏。一开始,你有 张卡牌,编号 到 ,你可以用这些卡牌来攻击敌人。你还有 点能量用来打出卡牌。

卡牌 的初始费用是 。打出卡牌 消耗的能量等于它的费用,会对敌人造成 点伤害。每打出一张牌后,剩下的牌的费用乘以 。

一张牌只能用一次。不能让能量变成负数。

在以上规则下,你可以从手牌中任意选择若干张,按任意顺序使用。
计算你能造成的最大总伤害。

  • ,,

分析

如果已经选定了要打出的牌,按什么顺序出牌消耗的能量最少?

按初始费用从高到低的顺序出牌。

把卡牌按初始费用从大到小排序。

考虑 DP。

子问题

:从前 张牌中选 张打出,消耗的能量不超过 ,最多能造成多少伤害?

,,。

状态太多。

优化

“每打出一张牌后,剩下的牌的费用乘以 ”,相当于“每打出一张牌后,剩下的牌的费用不变,玩家的能量减半”。

新的子问题

:从前 张牌中选一些打出,剩余的能量是 ,最多能造成多少伤害?

例题:价值衰减的背包问题

有 种物品。第 种物品的重量是 价值是 。每种物品有 个。

高桥要选一些物品放进一个容量是 的背包。他想让所选物品的价值之和最大又不想选太多个同种物品。于是他定义选 个第 种物品的满意度是 。

高桥想要选一些物品放进背包使得所有种类的总满意度最大而总重量不超过 。求可以达到的最大总满意度。

限制

子问题

:从前 种物品中选一些,所选物品的总重量不超过 ,总满意度的最大值。

最多能选 个第 种物品。有递推式

改写递推式

令 ,,那么 ,把上式改写成

整理成

把 记作 ,求 归结为求

一些一次函数的最大值

对 ,我们这样看:

  • 有 个一次函数,第 个()是 。
    求 。

求一些一次函数(直线)在某一点处的函数值的最大值。

例子:

  • 有些直线始终取不到最大值。
  • 可能取到最大值的直线,在其上取到最大值的 值是一个区间。直线的斜率越大,这个区间越靠右。

为什么是这样

考虑两个一次函数 ,,。

存在 使得

  • 当 时,
  • 当 时,

概要

  • 求 归结为求 。
  • 把 看作一次函数 在 处的值。
  • 把问题归结为求函数 在 处的值。
  • 用函数图像来研究函数 。

比较

  • 求 归结为求 。

  • 求 归结为求 。

考虑按 的顺序计算每个 。

和 相比

  • 多了一条直线 。它的斜率比 都更大。
  • 求值点右移。(从 变成 )

维护一列直线

假设我们维护了一列对求值点 有用的直线,按斜率从小到大排列。
那么 处函数值的最大值就是在这个序列中的第一个排列上取到的。

现在直线 来了,此后的求值点 满足 。

关键性质

设 。若 ,此后的最大值可以不在 上取到。因此我们可以把 扔掉。

例题:硬币问题

有 种硬币,第 种硬币有 个,单个的价值也为 。

从这些硬币中选出一些使得选中硬币的总价值恰好为 ,求有多少个不同方案。
答案模 。

如果对于任意一种硬币,两个方案选中的数量不同,那么算作两种不同方案。

限制

例题:翻转卡片 2

有 张卡片,编号 到 。
第 张卡片正面写着整数 ,背面写着整数 。这里,。

对于每个 ,解决下述问题。

张卡片正面向上放在桌面上。你可以把一些(可以是零张)卡片翻转(反转过后,卡片背面向上)。
为了让所有卡片向上那一面上的数字之和等于 ,最少要翻转多少张卡片?
如果无法通过翻转卡片让所有卡片向上那一面上的数字之和等于 ,输出 。

限制

例题:多重集合平均数

给定整数 ,,,对于每个整数 ,解决下述问题。

  • 求满足下列条件的非空多重集合 的个数除以 的余数:
    • 由 构成,每种数不超过 个(可以是 个)。
    • 里的数平均数是 。
限制
  • 是素数。

例题:Yet Another Knapsack Problem

有 种物品。第 种物品有 个,重量是 ,价值是 。

我们要从一共 个物品中选一些,所选物品的总重量不超过 。

对每个 ,求选 个物品,总价值可能达到的最大值。
在本题的限制条件下,对每个 ,一定能选出 个物品,总重量不超过 .

限制

# [abc375_e](https://atcoder.jp/contests/abc375/tasks/abc375_e) 3 Team Division

(CSP-S 2019 D2T1)

# [abc288_e](https://atcoder.jp/contests/abc288/tasks/abc288_e) Wish List

# [abc373_f](https://atcoder.jp/contests/abc373/tasks/abc373_f) Knapsack with Diminishing Values