前言
掌握动态规划(DP)是没有捷径的,咱们唯一能做的,就是投入时间猛猛刷题。好比学数学,只看书看视频而不做习题,是不能说学会的。
我能做的,是帮你节省找题的时间,并把这些题分类整理好。有着相同套路的题,一起做效率会更高,也更能领悟到 DP 的精髓。每个小节的题目已按照难度分排序(右侧数字为难度分)。

记忆化搜索是新手村神器(甚至可以用到游戏后期),推荐先看 动态规划入门:从记忆化搜索到递推。
但记忆化搜索并不是万能的,某些题目只有写成递推,才能结合数据结构等来优化时间复杂度,多数题目还可以优化空间复杂度。所以在写完记忆化搜索后,推荐把递推的代码也写一下。熟练之后直接写递推也可以。
有时候,思考「最后一步发生了什么」能启发我们找到子问题,进而得到状态定义和状态转移方程。
一、入门 DP
§1.1 爬楼梯
- 70. 爬楼梯
- 746. 使用最小花费爬楼梯 约 1500
- 3693. 爬楼梯 II 1560 注意:虽然题目说下标从 开始,但实际输入的数组还是从 开始的
- 377. 组合总和 Ⅳ 约 1600
- 2466. 统计构造好字符串的方案数 1694
- 2266. 统计打字方案数 1857
- 2533. 好二进制字符串的数量(会员题)同 2466 题
§1.2 打家劫舍
答疑
问:在 1:1 翻译的过程中,如何根据记忆化搜索,确定递推数组(DP 数组)的大小?为什么有时候要开 大小的数组,有时候要开 大小的数组?
答:看记忆化搜索的参数的范围(最小值和最大值)。例如 最小是 (递归边界也算),最大是 (递归入口),那么一共有 个不同的 ,就需要开 大小的 DP 数组。如果 最小是 ,最大是 ,一共有 个不同的 ,就需要开 大小的 DP 数组。
- 198. 打家劫舍
- 213. 打家劫舍 II 环形数组
- 2320. 统计放置房子的方式数 1608
- 3840. 打家劫舍 V 1619
- 740. 删除并获得点数
- 3186. 施咒的最大总伤害 1841
思维扩展:
- 2140. 解决智力问题 1709
§1.3 最大子数组和(最大子段和)
有两种做法:
- 定义状态 表示以 结尾的最大子数组和,不和 左边拼起来就是 ,和 左边拼起来就是 ,取最大值就得到了状态转移方程 ,答案为 。这个做法也叫做 Kadane 算法。
- 用 前缀和,转化成 121. 买卖股票的最佳时机。
- 53. 最大子数组和 可以看看我题解中的思考题
- 2606. 找到最大开销的子字符串 1422
- 1749. 任意子数组和的绝对值的最大值 1542
- 1191. K 次串联后最大子数组之和 1748
- 918. 环形子数组的最大和 1777 环形数组
- 2321. 拼接数组的最大分数 1791
思维扩展:
- 152. 乘积最大子数组
- 1186. 删除一次得到子数组最大和 1799
- 3381. 长度可被 K 整除的子数组的最大元素和 1943
- 3984. 可整除游戏 1944
- 3938. 矩阵中最大共享路径和 2252
思考题
完成本章后,请思考:什么时候要返回 ,什么时候要返回 ?
二、网格图 DP
对于一些二维 DP(例如背包、最长公共子序列),如果把 DP 矩阵画出来,其实状态转移可以视作在网格图上的移动。所以在学习相对更抽象的二维 DP 之前,做一些形象的网格图 DP 会让后续的学习更轻松(比如 0-1 背包的空间优化写法为什么要倒序遍历)。
§2.1 基础
- 64. 最小路径和
- 62. 不同路径
- 63. 不同路径 II
- 120. 三角形最小路径和
- 3393. 统计异或值为给定值的路径数目 1573
- 931. 下降路径最小和 1573
- 3603. 交替方向的最小路径代价 II 1639
- 2304. 网格中的最小路径代价 1658
- 1289. 下降路径最小和 II 1697 优化
- 3882. 网格图中最小异或路径 1771
- 3418. 机器人可以获得的最大金币数 1798
- 3742. 网格中得分最大的路径 1804
思维扩展:
- 2684. 矩阵中移动的最大次数 1626
- 1824. 最少侧跳次数 1778
§2.2 进阶
- 1594. 矩阵的最大非负积 1807
- 1301. 最大得分的路径数目 1853
- 3665. 统计镜子反射路径数目 1883
- 2435. 矩阵中和能被 K 整除的路径 1952
- 174. 地下城游戏
- 329. 矩阵中的最长递增路径
- 2328. 网格图中递增路径的数目 2001
- 2267. 检查是否有合法括号字符串路径 2085
- 1937. 扣分后的最大得分 2106
- 3363. 最多可收集的水果数目 实际难度 2200
- 1463. 摘樱桃 II
- 741. 摘樱桃
- 3459. 最长 V 形对角线段的长度 2337 状态设计
- 2510. 检查是否有路径经过相同数量的 0 和 1(会员题)
三、背包
§3.1 0-1 背包
每个物品只能选一次,即要么选,要么不选。所以 0-1 背包是「选或不选」的代表。
关于「枚举选哪个」的代表,见本题单的「§4.2 最长递增子序列」。
进阶:
- 474. 一和零 二维背包
- 3946. 购买最多物品数目 I 1728
- 3180. 执行操作可获得的最大总奖励 I 1849
- 3489. 零数组变换 IV 2068
- 1774. 最接近目标价格的甜点成本 非暴力做法
- 3685. 含上限元素的子序列和 2073
- 1049. 最后一块石头的重量 II 2092
- 879. 盈利计划 2204
- 3082. 求出所有子序列的能量和 2242
- 956. 最高的广告牌 2381
- 2518. 好分区的数目 2415
- 2742. 给墙壁刷油漆 2425 至少装满
- 3287. 求出数组中最大序列值 2545
- 3181. 执行操作可获得的最大总奖励 II 2688 bitset 优化
- LCP 47. 入场安检
- 2291. 最大股票收益(会员题)
- 2431. 最大限度地提高购买水果的口味(会员题)
- 3647. 两个袋子中的最大重量(会员题)多背包
- 2189. 建造纸牌屋的方法数(会员题)
§3.2 完全背包
物品可以重复选,无个数限制。
- 322. 零钱兑换
- 518. 零钱兑换 II
- 279. 完全平方数
- 3610. 目标和所需的最小质数个数(会员题)
- 3183. 达到总和的方法数量(会员题)混合背包
思维扩展:
- 3592. 硬币面值还原 1701
- 1449. 数位成本和为目标值的最大数字 1927
§3.3 多重背包(选做)
物品可以重复选,有个数限制。
求方案数
注意求方案数的题目不能用二进制优化。比如从 个相同物品中选 个,只有一种选法。但按照二进制优化,会把 分解为 ,有 和 两种选法。
如果要优化,可以考虑用 同余前缀和 优化。
- 2585. 获得分数的方法数 1910
- 3333. 找到初始输入字符串 II 2629
- 2902. 和带限制的子多重集合的数目 2759
二进制优化
§3.4 分组背包
同一组内的物品至多/恰好选一个。
- 1155. 掷骰子等于目标和的方法数 1654
- 1981. 最小化目标值与所选元素的差 2010
- 2218. 从栈中取出 K 个硬币的最大面值和 2158
§3.5 树上背包(选做)
注:目前力扣只有无依赖的背包,时间复杂度为 ,其中 是树的节点个数, 是背包容量。如果有依赖,可以优化到 。
- 3939. 统计有根树中不相邻子集的数目 2354
- 3562. 折扣价交易股票的最大利润 2458
四、经典线性 DP
§4.1 最长公共子序列(LCS)
一般定义 表示对 的求解结果。
§4.1.1 基础
思维扩展:
§4.1.2 进阶
- 3290. 最高乘法得分 1692
- 115. 不同的子序列
- 3628. 插入一个字母的最大子序列数 1754
- 1458. 两个子序列的最大点积 1824 如何表达「子序列非空」
- 3836. 恰好 K 个下标对的最大得分 1988
- 3316. 从原字符串里进行删除操作的最多次数 2062
- 1639. 通过给定词典构造目标字符串的方案数 2082
- 97. 交错字符串
- 1092. 最短公共超序列
- 44. 通配符匹配
- 10. 正则表达式匹配
思考题:
115 题的扩展。给定字符串 和 ,你可以在 的任意位置插入一个字母,插入后, 最多有多少个子序列等于 ?
思路和代码见 评论。
§4.2 最长递增子序列(LIS)
做法有很多:
- 枚举选哪个。(见讲解)
- 二分。(见讲解)
- 计算 和把 排序后的数组 的最长公共子序列。(用 LCS 求 LIS)
- 数据结构优化。(见 2407 题)
§4.2.1 基础
- 300. 最长递增子序列
- 334. 递增的三元子序列
- 2826. 将三个组排序 1721
- 1964. 找出到每个位置为止最长的有效障碍赛跑路线 1933
- 2111. 使数组 K 递增的最少操作次数 1941 包含进阶问题
§4.2.2 进阶
- 3825. 按位与结果非零的最长上升子序列 1846
- 1671. 得到山形数组的最少删除次数 1913
- 354. 俄罗斯套娃信封问题
- 1626. 无矛盾的最佳球队 2027
- 1691. 堆叠长方体的最大高度 2172
- 960. 删列造序 III 2247
- 3920. 删除元素后最大固定点数目 2264
- 2407. 最长递增子序列 II 2280
- 673. 最长递增子序列的个数
- 1187. 使数组严格递增 2316
- 1713. 得到子序列的最少操作次数 2351 用 LIS 求 LCS
- 3288. 最长上升路径的长度 2450
思维扩展:
- 368. 最大整除子集 约 1800
- 2901. 最长相邻不相等子序列 II 1899
- 3981. 统计从两个字符串形成目标字符串的不同方案数 2128
思考题:
给定整数 ,构造一个数组 ,使得 恰好有 个最长递增子序列。
五、划分型 DP
§5.1 判定能否划分
一般定义 表示长为 的前缀 能否划分。
枚举最后一个子数组的左端点 ,从 转移到 ,并考虑 是否满足要求。
§5.2 最优划分
计算最少(最多)可以划分出多少段、最优划分得分等。
一般定义 表示长为 的前缀 在题目约束下,分割出的最少(最多)子数组个数(或者定义成分割方案数)。
枚举最后一个子数组的左端点 ,从 转移到 ,并考虑 对最优解的影响。
- 132. 分割回文串 II
- 2707. 字符串中的额外字符 1736
- 3196. 最大化子数组的总成本 1847 也有状态机 DP 做法
- 2767. 将字符串分割为最少的美丽子字符串 1865
- 91. 解码方法
- 639. 解码方法 II
- LCR 165. 解密数字
- 1043. 分隔数组以得到最大和 1916
- 3144. 分割字符频率相等的最少子字符串 1917
- 1416. 恢复数组 1920
- 2472. 不重叠回文子字符串的最大数目 2013
- 1105. 填充书架 2014
- 2547. 拆分数组的最小代价 2020
- 3578. 统计极差最大为 K 的分割方式数 2033 优化
- 2430. 对字母串可执行的最大删除数 2102
- 2463. 最小移动总距离 2454
- 3579. 字符串转换需要的最小操作数 2493 可以做到
- 3500. 将数组分割为子数组的最小代价 2569 式子变形
- 2977. 转换字符串的最小成本 II 2696
- 3441. 变成好标题的最少代价 2765
- 2052. 将句子分隔成行的最低成本(会员题)
- 2464. 有效分割中的最少子数组数目(会员题)
§5.3 约束划分个数
将数组分成(恰好/至多) 个连续子数组,计算与这些子数组有关的最优值。
一般定义 表示将长为 的前缀 分成 个连续子数组所得到的最优解。
枚举最后一个子数组的左端点 ,从 转移到 ,并考虑 对最优解的影响。
注:对于恰好型划分 DP,可以通过控制内层循环的上下界,把时间复杂度从 优化至 。例如 3473 题。
- 813. 最大平均值和的分组 1937
- 3599. 划分数组得到最小 XOR 1955 做法不止一种
- 410. 分割数组的最大值
- 1278. 分割回文串 III 1979
- 1745. 分割回文串 IV
- 1335. 工作计划的最低难度 2035
- 1473. 粉刷房子 III 2056
- 2209. 用地毯覆盖后的最少白色砖块 2106
- 1478. 安排邮筒 2190
- 3473. 长度至少为 M 的 K 个子数组之和 2274 优化
- 1959. K 次调整数组大小浪费的最小总空间 2310
- 2478. 完美分割的方案数 2344
- 3538. 合并得到最小旅行时间 2461 相邻相关
- 3505. 使 K 个子数组内元素相等的最少操作数 2539
- 3077. K 个不相交子数组的最大能量值 2557 做法不止一种
- 2911. 得到 K 个半回文串的最少修改次数 2608
- 3117. 划分数组得到最小的值之和 2735
六、状态机 DP
一般定义 表示前缀 在状态 下的最优值。 一般很小。
§6.1 买卖股票
- 121. 买卖股票的最佳时机 交易 次
- 122. 买卖股票的最佳时机 II 交易次数不限
- 123. 买卖股票的最佳时机 III 交易 次
- 188. 买卖股票的最佳时机 IV 交易 次
- 3573. 买卖股票的最佳时机 V 交易 次 1777
- 309. 买卖股票的最佳时机含冷冻期 交易次数不限
- 714. 买卖股票的最佳时机含手续费 交易次数不限
§6.2 基础
- 198. 打家劫舍
- 3259. 超级饮料的最大强化能量 1484
- 2222. 选择建筑的方案数 1657
- 2708. 一个小组的最大实力值 做到 时间(联系 152 题)
- 1567. 乘积为正数的最长子数组长度 1710
- 2786. 访问数组中的位置使分数最大 1733
- 3952. 下标覆盖处的最大总和 1763
- 1911. 最大交替子序列和 1786
- 376. 摆动序列
- 3466. 最大硬币收集量(会员题)
§6.3 进阶
- 3628. 插入一个字母的最大子序列数 1754
- 3983. 一次替换后的子序列 1755
- 2771. 构造最长非递减子数组 1792
- 1186. 删除一次得到子数组最大和 1799
- 1594. 矩阵的最大非负积 1807
- 3738. 替换至多一个元素后最长非递减子数组 1811
- 3980. 变换二进制字符串的最少操作次数 1846 做法不止一种
- 3196. 最大化子数组的总成本 1847 做法不止一种
- 935. 骑士拨号器
- 3891. 最大化特殊下标数目的最少增加次数 1953
- 1537. 最大得分 1961
- 3976. 乘以系数后最大子数组和 1981
- 3811. 交替按位异或分割的数目 2006 做法不止一种
- 2919. 使数组变美的最小增量运算数 2031
- 3872. 替换最多一个元素后的最长等差子数组 2042
- 801. 使序列递增的最小交换次数 2066
- 3434. 子数组操作后的最大频率 2094
- 1955. 统计特殊子序列的数目 2125
- 3830. 移除至多一个元素后的最长交替子数组 2162
- 3068. 最大节点价值之和 2268
- 3640. 三段式数组 II 2278
- 2272. 最大波动的子字符串 2516
- 3661. 可以被机器人摧毁的最大墙壁数目 2525
- 3077. K 个不相交子数组的最大能量值 2557 做法不止一种
- 3743. 循环划分的最大得分 3125 环形数组
- LCP 19. 秋叶收藏集
- 276. 栅栏涂色(会员题)
- 1746. 经过一次操作后的最大子数组和(会员题)
- 2036. 最大交替子数组和(会员题)
- 2361. 乘坐火车路线的最少费用(会员题)
- 3269. 构建两个递增数组(会员题)
七、其他线性 DP
§7.1 一维 DP
发生在前缀/后缀之间的转移,例如从 转移到 ,或者从 转移到 。
- 3147. 从魔法师身上吸取的最大能量 1460
- 2944. 购买水果需要的最少金币数 1709
- 2140. 解决智力问题 1709 可以用刷表法
- 983. 最低票价 1786 有 做法
- 3989. 网格中保持一致的最大列数 约 1800
- 368. 最大整除子集 约 1850
- 2901. 最长相邻不相等子序列 II 1899
- 650. 两个键的键盘 约 2000
- 871. 最低加油次数 2074
- 3995. 转换字符串的最小成本 III 2101
- 32. 最长有效括号
- 2167. 移除所有载有违禁货物车厢所需的最少时间 2219
- 2188. 完成比赛的最少时间 2315
- 2896. 执行操作使两个字符串相等 做到
- 818. 赛车 2392
- 3389. 使字符频率相等的最少操作次数 2940
- 3464. 正方形上的点之间的最大距离
- 3205. 最大数组跳跃得分 I(会员题)有 做法
- 1259. 不相交的握手(会员题)
- 2597. 美丽子集的数目 用 DP 解决
- 2638. 统计 K-Free 子集的总数(会员题)上面这题的加强版
思维扩展:
- 4008. 击败所有怪物的最小初始强度 1776
§7.2 不相交区间
- 2830. 销售利润最大化 1851
- 2008. 出租车的最大盈利 1872
- 2054. 两个最好的不重叠活动 1883
- 1235. 规划兼职工作 2023 做法不止一种
- 1751. 最多可以参加的会议数目 II 2041
- 3414. 不重叠区间的最大得分 2723
§7.3 子数组 DP
- 53. 最大子数组和
- 152. 乘积最大子数组
- 1186. 删除一次得到子数组最大和 1799
- 3738. 替换至多一个元素后最长非递减子数组 1811
- 3524. 求出数组的 X 值 I 2008 刷表法
- 3872. 替换最多一个元素后的最长等差子数组 2042
- 3830. 移除至多一个元素后的最长交替子数组 2162
- 3448. 统计可以被最后一个数位整除的子字符串数目 2387 刷表法
思维扩展:
- 2262. 字符串的总引力 2033
- 828. 统计子串中的唯一字符 2034
- 467. 环绕字符串中唯一的子字符串
§7.4 合法子序列 DP
计算合法子序列的最长长度、个数、元素和等。
一般定义 表示以元素 结尾的合法子序列的最长长度/个数/元素和,从子序列的倒数第二个数转移过来。
注意这里的 不是下标,是元素值。如果 不是整数,或者值域范围很大,可以用哈希表代替数组。
- 2501. 数组中最长的方波 1480
- 1218. 最长定差子序列 1597
- 2826. 将三个组排序 1721 做法不止一种
- 1027. 最长等差数列 1759
- 2370. 最长理想子序列 1835
- 873. 最长的斐波那契子序列的长度 1911
- 3686. 稳定子序列的数量 1969
- 3202. 找出有效子序列的最大长度 II 1974
- 446. 等差数列划分 II - 子序列 求个数
- 3811. 交替按位异或分割的数目 2006 做法不止一种
- 3351. 好子序列的元素之和 2086 求和
- 3041. 修改数组后最大化数组中的连续元素数目 2231
- 3409. 最长相邻绝对差递减子序列 2500 状态设计
- 3098. 求出所有子序列的能量和 2553
- 2901. 最长相邻不相等子序列 II 做到线性复杂度
- 3299. 连续子序列的和(会员题)
思维扩展:
- 1048. 最长字符串链 1599
- 940. 不同的子序列 II 1985
- 1987. 不同的好子序列数目 2422 思考方式同 940 题
- 730. 统计不同回文子序列
§7.5 子矩形 DP
- 3148. 矩阵中的最大得分 1820
- 221. 最大正方形
- 4016. 两个不重叠子正方形的最大面积 1959
- 1277. 统计全为 1 的正方形子矩阵
- 2088. 统计农场中肥沃金字塔的数目 2105
- 3197. 包含所有 1 的最小矩形面积 II 做到
§7.6 多维 DP
不会设计状态?那你可要好好刷这一节了。
- 2222. 选择建筑的方案数 1657 把 改成 怎么做?
- 2826. 将三个组排序 1721 做法不止一种
- 2400. 恰好移动 k 步到达某一位置的方法数目 1751
- 1262. 可被三整除的最大和 1762
- 3780. 能被 3 整除的三元组最大和 通用做法
- 3332. 旅客可以得到的最多点数 1828
- 3176. 求出最长好子序列 I 1849
- 1269. 停在原地的方案数 1854
- 3250. 单调数组对的数目 I 1898
- 3218. 切蛋糕的最小总开销 I 也有贪心做法
- 3122. 使矩阵满足条件的最少操作次数 1905
- 576. 出界的路径数
- 403. 青蛙过河
- 3850. 统计结果等于 K 的序列数目 1965
- 3725. 统计每一行选择互质整数的方案数 1982
- 1223. 掷骰子模拟 2008
- 1320. 二指输入的的最小距离 2028
- 3366. 最小数组和 2040
- 1575. 统计所有可行路径 2055
- 3154. 到达第 K 级台阶的方案数 2071
- 2318. 不同骰子序列的数目 2090
- 3469. 移除所有数组元素的最小代价 2112 状态设计
- 2746. 字符串连接删减字母 2126
- 1444. 切披萨的方案数 2127
- 3320. 统计能获胜的出招序列数 2153
- 3429. 粉刷房子 IV 2166
- 2896. 执行操作使两个字符串相等 2172
- 1420. 生成数组 2176
- 3193. 统计逆序对的数目 2266
- 3892. 产生至少 K 个峰值的最少操作次数 2280
- 2312. 卖木头块 2363
- 3177. 求出最长好子序列 II 2365
- 1884. 鸡蛋掉落-两枚鸡蛋
- 887. 鸡蛋掉落 2377
- 920. 播放列表的数量 2400
- 514. 自由之路 做到
- 3336. 最大公约数相等的子序列数量 2403
- 1388. 3n 块披萨 2410
- 903. DI 序列的有效排列 2433
- 1900. 最佳运动员的比拼回合 2455
- 4009. 最小化最大可能等待时间 2499
- 1531. 压缩字符串 II 2576
- 1883. 准时抵达会议现场的最小跳过休息次数 2588 避免浮点运算的技巧
- 964. 表示数字的最少运算符 2594
- 1787. 使所有区间的异或结果为零 2640
- 3509. 最大化交错和为 K 的子序列乘积 2703
- 3441. 变成好标题的最少代价 2765
- 2060. 同源字符串检测 2804
- 2809. 使数组和小于等于 x 的最少时间 2979
- 3003. 执行操作后的最大分割数量 3039 如何分析时间复杂度
- 3225. 网格图操作后的最大分数 3028 IOI2022 原题
- LCP 57. 打地鼠
- LCP 43. 十字路口的交通
- LCP 65. 舒适的湿度
- LCP 36. 最多牌组数
- LCP 38. 守卫城堡
- 256. 粉刷房子(会员题)
- 265. 粉刷房子 II(会员题)
- 3339. 查找 K 偶数数组的数量(会员题)
- 568. 最大休假天数(会员题)
- 1692. 计算分配糖果的不同方式(会员题)
- 2143. 在两个数组的区间中选取数字(会员题)
- 3269. 构建两个递增数组(会员题)
思维扩展:
§7.7 计数 DP
- 1079. 活字印刷 做到
- 1866. 恰有 K 根木棍可以看到的排列数目 2333
- 3317. 安排活动的方案数 2414
- 3343. 统计平衡排列的数目 2615
- 3539. 魔法序列的数组乘积之和 2694
八、区间 DP
从数组的左右两端不断缩短,求解关于某段下标区间的最优值。
一般定义 表示下标区间 的最优值。
§8.1 最长回文子序列
- 516. 最长回文子序列
- 1312. 让字符串成为回文串的最少插入次数 1787
- 3472. 至多 K 次操作后的最长回文子序列 1884
- 1771. 由子序列构造的最长回文串的长度 2182
- 730. 统计不同回文子序列
- 1682. 最长回文子序列 II(会员题)
- 1216. 验证回文串 III(会员题)
- 1246. 删除回文子数组(会员题)
§8.2 区间 DP
对于类似合法括号字符串(RBS)的消除问题,通常根据题意,会有如下性质:
- 可以消除相邻的匹配字符。
- 相邻匹配字符消除后,原本不相邻的字符会变成相邻,可以继续消除。换句话说,设子串 ,如果 和 是匹配的(可以消除),且子串 可以完全消除,那么子串 可以完全消除。
- 设子串 ,如果子串 和 可以完全消除,那么子串 可以完全消除。
满足上述性质的题目(例如 3563 题),可以用区间 DP 解决。
定义 表示消除 到 的最优值。
- 根据性质 2,可以把 缩小成子问题 。
- 根据性质 3,可以枚举子串 的右端点,即枚举 ,把 划分成子问题 和 。注意这里枚举 的步长是 ,因为每次消除 个字符,被消除的子串长度一定是偶数。
边界:,即空串。
答案:。
- 5. 最长回文子串
- 647. 回文子串
- 3040. 相同分数的最大操作数目 II 1709
- 1130. 叶值的最小代价生成树 1919
- 96. 不同的二叉搜索树
- 375. 猜数字大小 II
- 1770. 执行乘法运算的最大分数 2068
- 1547. 切棍子的最小成本 2116
- 1039. 多边形三角剖分的最低得分 2130
- 1000. 合并石头的最低成本 2423
- 2019. 解出数学表达式的学生分数 2584
- 面试题 08.14. 布尔运算
- 3563. 移除相邻字符后字典序最小的字符串 2585
- 3277. 查询子数组最大异或值 2693
- 87. 扰乱字符串
- 312. 戳气球
- 664. 奇怪的打印机
- 546. 移除盒子 同 CF1107E
- 471. 编码最短长度的字符串(会员题)
- 3018. 可处理的最大删除操作数 I(会员题)
还有一些区间 DP 问题,见本题单的「十四、博弈 DP」。
九、状态压缩 DP(状压 DP)
§9.1 排列型状压 DP ① 相邻无关
学习指南:
暴力做法是枚举所有排列,对每个排列计算和题目有关的值,时间复杂度(通常来说)是 。可以解决 的问题。
状压 DP 可以把时间复杂度(通常来说)优化至 。可以解决 的问题。
一般有两种定义方式:
- 定义 表示已经排列好的元素(下标)集合为 时,和题目有关的最优值。通过枚举当前位置要填的元素(下标)来转移。
- 定义 表示可以选的元素(下标)集合为 时,和题目有关的最优值。通过枚举当前位置要填的元素(下标)来转移。
注:部分题目由于暴搜+剪枝也能过,难度分仅供参考。
- 526. 优美的排列
- 3376. 破解锁的最少时间 I
- 1879. 两个数组最小的异或值之和 2145
- 2850. 将石头分散到网格图的最少移动次数
- 1947. 最大兼容性评分和
- 1799. N 次操作后的最大分数和
- 3533. 判断连接可整除性 2257 字典序 暴搜
- 3530. 有向无环图中合法拓扑排序的最大利润 2353 拓扑序
- 2172. 数组的最大与和 2392
- 2992. 自整除排列的数量(会员题)
- 2403. 杀死所有怪物的最短时间(会员题)同 3376 题
- 1066. 校园自行车分配 II(会员题)
§9.2 排列型状压 DP ② 相邻相关
一般定义 表示未选(或者已选)的集合为 ,且上一个填的元素(下标)为 时,和题目有关的最优值。通过枚举当前位置要填的元素(下标)来转移。
时间复杂度(通常来说)是 。
- 2741. 特别的排列 2021
- 996. 平方数组的数目
- 1681. 最小不兼容性 2390 见我在官解下的评论
- 3615. 图中的最长回文路径 2463
- 3283. 吃掉所有兵需要的最多移动次数 2473
- 3149. 找出分数最低的排列 2642
§9.3 旅行商问题(TSP)
本质上就是排列型 ②。
- 943. 最短超级串 2186
- 847. 访问所有节点的最短路径 2201
- LCP 13. 寻宝
- 2247. K 条高速公路的最大旅行费用(会员题)
相似问题:
- 3568. 清理教室的最少移动 2143
- 864. 获取所有钥匙的最短路径 2259
§9.4 子集状压 DP
一般定义 表示未选(或者已选)的集合为 时,和题目有关的最优值。通过枚举 (或者 的补集 )的子集来转移。
时间复杂度(通常来说)是 ,证明:
对于大小为 的集合,它的大小为 的子集有 个,每个子集又有 个子集。根据二项式定理,,所以「枚举子集的子集」的总体时间复杂度为 。
注:某些网格图的题也可以用「选或不选」来做,从而减少状态个数,详见「§9.5 轮廓线 DP」。
- 2305. 公平分发饼干 1887
- 1986. 完成任务的最少工作时间段 1995
- 3670. 没有公共位的整数最大乘积 2234
- 1723. 完成所有工作的最短时间 2284
- 1655. 分配重复整数 2307
- 3444. 使数组包含目标值倍数的最少增量 2337
- 3575. 最大好子树分数 2360
- 1349. 参加考试的最大学生数 2386
- 1681. 最小不兼容性 2390 有 做法
- 3801. 合并有序列表的最小成本 2399
- 2572. 无平方子集计数 2420
- 1994. 好子集的数目 2465
- 1494. 并行课程 II
- LCP 04. 覆盖
- LCP 53. 守护太空城
- 465. 最优账单平衡(会员题)
- 2152. 穿过所有点的所需最少直线数量(会员题)
相关问题:
- 3594. 所有人渡河所需的最短时间 2604
§9.5 轮廓线 DP
- 1411. 给 N x 3 网格图涂色的方案数 1845
- 1931. 用三种不同颜色为网格涂色 2170
- 1349. 参加考试的最大学生数 2386
- 1659. 最大化网格幸福感 2655
- 980. 不同路径 III
- LCP 04. 覆盖
- LCP 76. 魔法棋盘
- 2184. 建造坚实的砖墙的方法数(会员题)
思维扩展:
- 756. 金字塔转换矩阵 1990
§9.6 SOS DP
子集和 DP(Sum Over Subsets DP,SOS DP),国内算法竞赛圈一般叫高维前缀和。
模板(从子集转移过来):
# 设 w 为 a[i] 的二进制最大长度
# 返回一个长为 2^w 的数组 f,其中 f[S] 表示 a 中是 S 的子集的元素个数(把二进制数视作集合)
# 时间复杂度 O(n + U log U),其中 U = max(a)
def sos_dp(a: List[int]) -> List[int]:
mx = max(a)
w = mx.bit_length() # 二进制长度上限
u = 1 << w
f = [0] * u
for x in a:
f[x] += 1 # 初始值
for i in range(w):
bit = 1 << i
s = 0
while s < u:
s |= bit # 优化:快速跳到 i 位是 1 的 s
f[s] += f[s ^ bit]
s += 1
return f模板(从超集转移过来):
# 设 w 为 a[i] 的二进制最大长度
# 返回一个长为 2^w 的数组 f,其中 f[S] 表示 a 中是 S 的超集的元素个数(把二进制数视作集合)
# 时间复杂度 O(n + U log U),其中 U = max(a)
def sos_dp(a: List[int]) -> List[int]:
mx = max(a)
w = mx.bit_length() # 二进制长度上限
u = 1 << w
f = [0] * u
for x in a:
f[x] += 1 # 初始值
for i in range(w):
bit = 1 << i
s = 0
while s < u:
s |= bit # 优化:快速跳到 i 位是 1 的 s
f[s ^ bit] += f[s]
s += 1
return f- 3670. 没有公共位的整数最大乘积 2234
- 2002. 两个回文子序列长度的最大乘积
- 2044. 统计按位或能得到最大值的子集数目
- 2732. 找到矩阵中的好子集
- 3757. 有效子序列的数量 2519
§9.7 其他状压 DP
- 473. 火柴拼正方形
- 698. 划分为k个相等的子集
- 1125. 最小的必要团队 2251
- 1434. 每个人戴不同帽子的方案数 2273
- 464. 我能赢吗
- 691. 贴纸拼词
- 3276. 选择矩阵中单元格的最大得分 2403 横看成岭侧成峰
- 1595. 连通两组点的最小成本 2538
- 1815. 得到新鲜甜甜圈的最多组数 2559
- LCP 69. Hello LeetCode!
- LCP 82. 万灵之树
- 351. 安卓系统手势解锁(会员题)
十、数位 DP
用于解决 范围内有多少个符合要求的元素,或者符合要求的元素之和等问题。
§10.1 统计合法元素的数目
数位 DP v2.0 模板讲解(上下界数位 DP)
问:下面的代码,为什么只在 !limitLow && !limitHigh 成立时才记忆化?(Python 选手可以跳过这个问题)
答:记忆化的原理是,当我们再次遇到相同状态时,可以直接返回 中保存的结果。数位 DP 本质是暴力枚举,枚举每个数位填什么。 是我们枚举的第一个数, 是我们枚举的最后一个数。所以「填入的数字组成了 」以及「填入的数字组成了 」在整个递归过程中只会枚举一次。状态 中的 和 如果其中一个是 ,说明我们正在填 或者正在填 ,这样的状态只会出现一次,不会再次遇到,所以不需要记忆化这种状态。
下面是数位 DP v2.1 模板。相比 v2.0,不需要写 参数(之前是否填过数字)。
注:只有上界约束的题目,相当于 或者 。
# 代码示例:返回 [low, high] 中的恰好包含 target 个 0 的整数个数
# 比如 digitDP(0, 10, 1) == 2
# 要点:我们统计的是 0 的个数,需要区分「前导零」与「整数中的零」,前导零不能计入,而整数中的零需要计入
def digitDP(low: int, high: int, target: int) -> int:
low_s = list(map(int, str(low))) # 避免在 dfs 中频繁调用 int()
high_s = list(map(int, str(high)))
n = len(high_s)
diff_lh = n - len(low_s)
@cache
def dfs(i: int, cnt0: int, limit_low: bool, limit_high: bool) -> int:
if cnt0 > target:
return 0 # 不合法
if i == n:
return 1 if cnt0 == target else 0
lo = low_s[i - diff_lh] if limit_low and i >= diff_lh else 0
hi = high_s[i] if limit_high else 9
res = 0
start = lo
# 通过 limit_low 和 i 可以判断能否不填数字,dfs 无需 is_num 参数
# 如果前导零不影响答案,去掉这个 if block
if limit_low and i < diff_lh:
# 不填数字,上界不受约束
res = dfs(i + 1, 0, True, False)
start = 1 # 下面填数字,从 1 开始填
# is_num = not limit_low or i > diff_lh
for d in range(start, hi + 1):
res += dfs(i + 1,
cnt0 + (1 if d == 0 else 0), # 统计 0 的个数
limit_low and d == lo,
limit_high and d == hi)
# res %= MOD
return res
return dfs(0, 0, True, True)- 3747. 统计移除零后不同整数的数目 1848
- 2719. 统计整数数目
- 762. 二进制表示中质数个计算置位 非暴力做法
- 1399. 统计最大组的数目 非暴力做法
- 1742. 盒子中小球的最大数量 非暴力做法
- 788. 旋转数字 非暴力做法
- 902. 最大为 N 的数字组合 1990
- 600. 不含连续 1 的非负整数
- 3966. 统计范围内的好整数 2006
- 2376. 统计特殊整数 2120
- 357. 统计各位数字都不同的数字个数 同 2376 题
- 3791. 给定范围内平衡整数的数目 2132
- 3906. 统计网格路径中好整数的数目 2160
- 1012. 至少有 1 位重复的数字 2230
- 3519. 统计逐位非递减的整数 2246 进制转换
- 3869. 统计区间内奇妙数的数目 2255
- 2827. 范围中美丽整数的数目 2324
- 2999. 统计强大整数的数目 2351
- 2801. 统计范围内的步进数字数目 2367
- 2843. 统计对称整数的数目 非暴力做法
- 3352. 统计小于 N 的 K 可约简整数 2451
- 3621. 位计数深度为 K 的整数数目 I 同 3352 题
- 3490. 统计美丽整数的数目 2502
- 1397. 找到所有好字符串 2667
- 1215. 步进数(会员题)
- 3032. 统计各位数字都不同的数字个数 II(会员题)
从低到高:
- 3704. 统计和为 N 的无零数对 2419 数对
§10.2 统计合法元素的价值总和
每个元素 都有一个相应的价值 ,计算 中的满足题目约束的元素的价值总和。
例如 是计算满足题目约束的元素和, 是计算满足题目约束的元素的数位和。
模板(数位和):
# 计算在 [low, high] 中的整数 x 的数位和,满足 x 中的不同数字个数不超过 k
def digitDPContribution(low: int, high: int, k: int) -> int:
low_s = list(map(int, str(low))) # 避免在 dfs 中频繁调用 int()
high_s = list(map(int, str(high)))
n = len(high_s)
diff_lh = n - len(low_s)
# dfs 返回两个数:子树合法整数个数,子树数位总和
@cache
def dfs(i: int, mask: int, limit_low: bool, limit_high: bool) -> Tuple[int, int]:
if i == n:
# 如果没有特殊约束,那么能递归到终点的都是合法整数
return 1, 0
lo = low_s[i - diff_lh] if limit_low and i >= diff_lh else 0
hi = high_s[i] if limit_high else 9
cnt = res = 0
start = lo
# 如果前导零不影响答案,去掉这个 if block
if limit_low and i < diff_lh:
# 不填数字,上界不受约束
cnt, res = dfs(i + 1, 0, True, False)
start = 1
for d in range(start, hi + 1):
new_mask = mask | 1 << d
if new_mask.bit_count() > k: # 不满足要求
continue
sub_cnt, sub_sum = dfs(i + 1,
new_mask,
limit_low and d == lo,
limit_high and d == hi)
cnt += sub_cnt # 累加子树的合法整数个数
res += sub_sum # 累加子树的数位总和
res += d * sub_cnt # d 会出现在 sub_cnt 个数中(贡献法)
# cnt %= MOD; res %= MOD
return cnt, res
return dfs(0, 0, True, True)[1]- 3871. 统计范围内的逗号 II 做法不止一种
- 233. 数字 1 的个数
- 面试题 17.06. 2 出现的次数
- 3954. 区间内的兼容数字之和 I 非暴力做法
- 3007. 价值和小于等于 K 的最大数字 2258
- 3753. 范围内总波动值 II 2297
- 1067. 范围内的数字计数(会员题)
§10.3 其他数位 DP
- 3677. 统计二进制回文数字的数目 2223
- 3348. 最小可整除数位乘积 II 3101 只有下界
十一、优化 DP
§11.1 前缀和优化 DP
- 1871. 跳跃游戏 VII 1896 做法不止一种
- 2327. 知道秘密的人数 做到
- 3699. 锯齿形数组的总数 I 2123
- 3883. 统计满足数位和数组的非递减数组数目 2172
- 1997. 访问完所有房间的第一天 2260
- 629. K 个逆序对数组
- 3193. 统计逆序对的数目 2266
- 3473. 长度至少为 M 的 K 个子数组之和 2274 划分型
- 3251. 单调数组对的数目 II 2323 做法不止一种
- 2478. 完美分割的方案数 2344
- 837. 新 21 点 2350 也可以用滑动窗口
- 3797. 统计在矩形格子里移动的路径数目 2376
- 3077. K 个不相交子数组的最大能量值 2557 划分型
- 3333. 找到初始输入字符串 II 2629 做法不止一种
- 2902. 和带限制的子多重集合的数目 2759
- 1977. 划分数字的方案数 2817
- 3130. 找出所有稳定的二进制数组 II 2825 做法不止一种
§11.2 单调栈优化 DP
前置题单:单调栈(矩形系列/字典序最小/贡献法)
- 1335. 工作计划的最低难度 2035
- 2866. 美丽塔 II 2072
- 2617. 网格图中最少访问的格子数 2582
- 2355. 你能拿走的最大图书数量(会员题)
§11.3 单调队列优化 DP
一般用来维护一段转移来源的最值。
- 前提:区间右端点变大时,左端点也在变大(同滑动窗口)。
- 转移前,去掉队首无用数据。
- 计算转移(直接从队首转移)。
- 把数据(一般是 )插入队尾前,去掉队尾无用数据。
- 2944. 购买水果需要的最少金币数 1709 做到
- 1696. 跳跃游戏 VI 1954
- 1425. 带限制的子序列和 2032
- 3578. 统计极差最大为 K 的分割方式数 2033
- 3956. 非重叠子数组最大和 I 2061
- 1140. 石子游戏 II 做到
- 375. 猜数字大小 II 做到
- 1687. 从仓库到码头运输箱子 2610
- 2463. 最小移动总距离 做到
- 3117. 划分数组得到最小的值之和 2735
- 2945. 找到最大非递减数组的长度 2943
- 2969. 购买水果需要的最少金币数 II(会员题)
§11.4 树状数组/线段树优化 DP
- 1626. 无矛盾的最佳球队 2027
- 2407. 最长递增子序列 II 2280
- 3915. 距离至少为 K 的交替子序列的最大和 2288
- 2770. 达到末尾下标所需的最大跳跃次数 做到
- 2926. 平衡子序列的最大和 2448 LIS 最大和
- 2547. 拆分数组的最小代价 做到
- 3671. 子序列美丽值求和 2647
- 2916. 子数组不同元素数目的平方和 II 2816 思路类似 2547 题
§11.5 字典树优化 DP
§11.6 矩阵快速幂优化 DP
有两种类型的矩阵快速幂优化 DP:
- 线性 DP(常系数齐次线性递推),转移系数写在第一行,其余行 ,例如 70,1137。讲解(含模板代码)。
- 多维 DP 或者状态机 DP,转移系数可表示为一个 大小的矩阵,例如 935。讲解(含模板代码)。
时间复杂度一般为 。
进阶做法:先用 Berlekamp-Massey 算法找规律,得到线性递推式,然后用 Kitamasa 算法(或者 Bostan-Mori 算法)优化。
请看我的知乎科普文章:
具体例子见 3700 题 我的题解的方法二。
时间复杂度一般为 。
- 70. 爬楼梯
- 509. 斐波那契数
- 1137. 第 N 个泰波那契数
- 1220. 统计元音字母序列的数目
- 552. 学生出勤记录 II
- 935. 骑士拨号器
- 790. 多米诺和托米诺平铺
- 1411. 给 N x 3 网格图涂色的方案数
- 1931. 用三种不同颜色为网格涂色
- 3337. 字符串转换后的长度 II 2412
- 3700. 锯齿形数组的总数 II 2435
- 2851. 字符串转换 2858
- 2912. 在网格上移动到目的地的方法数(会员题)
§11.7 斜率优化 DP
也叫凸包优化/凸壳优化(CHT,Convex Hull Trick)。
- 3826. 最小分割分数 2345
- 3494. 酿造药水需要的最少总时间
- 3500. 将数组分割为子数组的最小代价
- 3693. 爬楼梯 II 与 无关的做法
§11.8 WQS 二分优化 DP
本质是凸优化中的拉格朗日松弛 (Lagrangian Relaxation)。
把最多选 个物品的问题(时间复杂度高)转换成选任意个物品的问题(时间复杂度低)。
一般时间复杂度为 或者 。
- 188. 买卖股票的最佳时机 IV
- 3573. 买卖股票的最佳时机 V
- 2209. 用地毯覆盖后的最少白色砖块
- 3957. 非重叠子数组最大和 II 2611
- 3826. 最小分割分数
- 3743. 循环划分的最大得分
- 3929. 最小分割分数 II(会员题)同 3826 题
此外「§5.3 约束划分个数」的部分题目也可以用 WQS 二分优化。
§11.9 其他优化 DP
- 3654. 删除可整除和后的最小数组和 2039
- 1937. 扣分后的最大得分 2106
- 2713. 矩阵中严格递增的单元格数 2387
- 3651. 带传送的最小路径成本 2411
- 2318. 不同骰子序列的数目 容斥优化(类似 CF1943D2)
- 1563. 石子游戏 V 单调性优化,可以做到
- 3181. 执行操作可获得的最大总奖励 II 2688 bitset 优化
- 2267. 检查是否有合法括号字符串路径 可以进一步地用 bitset 优化
- 3213. 最小代价构造字符串 字符串哈希 / 后缀数组 / AC 自动机优化
- 3292. 形成目标字符串需要的最少字符串数 II 字符串哈希 / AC 自动机优化
- 3743. 循环划分的最大得分 闵可夫斯基和
- LCP 14. 切分数组
- LCP 59. 搭桥过河 Slope Trick
- 2263. 数组变为有序的最小操作次数(会员题)Slope Trick
- 3916. 锯齿形数组的总数 III(会员题)拉格朗日插值
关于倍增算法,见 题单 的「§3.8 最近公共祖先(LCA)、倍增算法」。
十二、树形 DP
注:可能有同学觉得树形 DP 没有重复访问同一个状态(重叠子问题),并不能算作 DP,而是算作普通的递归。这么说也有一定道理,不过考虑到思维方式和 DP 是一样的自底向上,所以仍然叫做树形 DP。此外,如果是自顶向下的递归做法,是存在重叠子问题的,一般要结合记忆化搜索实现。
§12.1 树的直径
- 543. 二叉树的直径
- 687. 最长同值路径
- 124. 二叉树中的最大路径和
- 2385. 感染二叉树需要的总时间 1711
- 2246. 相邻字符不同的最长路径 2126
- 3203. 合并两棵树后的最小直径 2266
- 1617. 统计子树中城市之间最大距离 2309
- 2538. 最大价值和与最小价值和的差值 2398
- 1522. N 叉树的直径(会员题)
- 1245. 树的直径(会员题)
- 549. 二叉树最长连续序列 II(会员题)
- 3372. 连接两棵树后最大目标节点数目 I 算法优化
注:求直径也有两次 DFS 的做法。
§12.2 树上最大独立集
- 337. 打家劫舍 III(没有上司的舞会)
- 2646. 最小化旅行的价格总和 2238
- 3544. 子树反转和 2545 记忆化搜索 / 树上刷表法
- 2378. 选择边来最大化树的得分(会员题)
- 3949. 子树反转和 II(会员题)
§12.3 树上最小支配集
讲解:树形 DP:监控二叉树【基础算法精讲 25】,包含 968 的变形题。
- 968. 监控二叉树 2124
§12.4 换根 DP
也叫二次扫描法。
设 是 的子节点。从「以 为整棵树的根」变成「以 为整棵树的根」,发生了什么?
- 站在 的视角,去掉子树 。设剩余部分为 。
- 以 为根时, 挂在 的下面, 变成 的一棵子树。
模板一:简单场景(例如 3772 题)
class Solution:
def maxSubgraphScore(self, n: int, edges: List[List[int]], good: List[int]) -> List[int]:
g = [[] for _ in range(n)]
for x, y in edges:
g[x].append(y)
g[y].append(x)
# sub_score[x] 表示(以 0 为根时)包含 x 的子树 x 的最大得分
sub_score = [0] * n
# 计算 sub_score[x]
def dfs(x: int, fa: int) -> None:
sub_score[x] = 1 if good[x] else -1 # sub_score[x] 一定包含 x
for y in g[x]:
if y != fa:
dfs(y, x)
# 如果子树 y 的最大得分 > 0,选子树 y,否则不选
sub_score[x] += max(sub_score[y], 0)
dfs(0, -1)
ans = [0] * n
ans[0] = sub_score[0]
# 对于 x 的儿子 y,计算包含 y 的子图最大得分
def reroot(x: int, fa: int) -> None:
for y in g[x]:
if y != fa:
# 从 ans[x] 中去掉子树 y,剩余部分记作 F
score_f = ans[x] - max(sub_score[y], 0)
# 换根后,x 挂在 y 的下面,F 变成 y 的一棵子树
# 计算 F 对 y 的贡献:如果 F 的最大得分 > 0,选 F,否则不选
ans[y] = sub_score[y] + max(score_f, 0)
reroot(y, x)
reroot(0, -1)
return ans模板二:维护最大次大(例如 3241 题)
class Solution:
def timeTaken(self, edges: List[List[int]]) -> List[int]:
n = len(edges) + 1
g = [[] for _ in range(n)]
for x, y in edges:
g[x].append(y)
g[y].append(x)
# sub_res[x] 保存子树 x 的最大深度,次大深度,以及最大深度要往哪个儿子走
sub_res = [None] * n
# 计算 sub_res[x]
def dfs(x: int, fa: int) -> None:
max_d = max_d2 = my = 0
for y in g[x]:
if y == fa:
continue
dfs(y, x)
w = 2 - y % 2 # 从 x 到 y 的边权
max_y = sub_res[y][0] + w # 从 x 出发,往 y 方向的最大深度
if max_y > max_d:
max_d2 = max_d
max_d = max_y
my = y
elif max_y > max_d2:
max_d2 = max_y
sub_res[x] = (max_d, max_d2, my)
dfs(0, -1)
# ans[x] 表示当 x 是树根时,整棵树的最大深度
ans = [0] * n
# 计算 ans[x]
def reroot(x: int, fa: int, from_up: int) -> None:
max_d, max_d2, my = sub_res[x]
ans[x] = max(max_d, from_up)
for y in g[x]:
if y == fa:
continue
w = 2 - x % 2 # 从 y 到 x 的边权
mx = max_d if y != my else max_d2
# 站在 x 的角度,不往 y 走,能走多远?
# 要么往上走(from_up),要么往除了 y 的其余子树走(mx),二者取最大值
reroot(y, x, max(from_up, mx) + w) # 对于 y 来说,加上从 y 到 x 的边权 w
reroot(0, -1, 0)
return ans- 834. 树中距离之和 2197
- 2581. 统计可能的树根数目 2228
- 3772. 子图的最大得分 2235
- 2858. 可以到达每一个节点的最少边反转次数 2295
- 310. 最小高度树 也可以用拓扑排序做
- 3241. 标记所有节点需要的时间 2522
- 3372. 连接两棵树后最大目标节点数目 I 做到 ,解决 和 很大但 很小的场景
- 3967. 任务完成时间 II(会员题)
注:前后缀分解,可以视作一条链上的换根 DP。
§12.5 其他树形 DP
- 2925. 在树上执行操作以后得到的最大分数 1940
- 3068. 最大节点价值之和 2268
- 2920. 收集所有金币可获得的最大积分 2351 记忆化搜索
- 3575. 最大好子树分数 2360
- 3562. 折扣价交易股票的最大利润 2458 背包
- 1916. 统计为蚁群构筑房间的不同顺序 2486
- 3367. 移除边之后的权重最大和 2602
- LCP 10. 二叉树任务调度
- LCP 34. 二叉树染色
- LCP 64. 二叉树灯饰
- 2313. 二叉树中得到结果所需的最少翻转次数(会员题)
十三、图 DP
- 3243. 新增道路查询后的最短距离 I 1568
- 787. K 站中转内最便宜的航班 1786
- 3620. 恢复网络路径 1998
- 1786. 从第一个节点出发到最后一个节点的受限路径数 2079
- 2050. 并行课程 III 2084
- 1976. 到达目的地的方案数 2095
- 3977. 有限电量到达目标节点的最少时间 2103
- 3543. K 条边路径的最大边权和 2110 bitset
- 1857. 有向图中最大颜色值 2313
- 1928. 规定时间内到达终点的最小花费 2413
- 913. 猫和老鼠 2800 拓扑序
- 1728. 猫和老鼠 II 2849 拓扑序
- LCP 07. 传递信息
- 1548. 图中最相似的路径(会员题)
另见【题单】图论算法 中的「全源最短路:Floyd」,本质是多维 DP。
十四、博弈 DP
- 1025. 除数博弈 1435 有数学做法
- 486. 预测赢家
- 877. 石子游戏 1590 有数学做法
- 1510. 石子游戏 IV 1787
- 1690. 石子游戏 VII 1951
- 1406. 石子游戏 III 2027
- 1140. 石子游戏 II 2035
- 1563. 石子游戏 V 2087
- 464. 我能赢吗
- 1872. 石子游戏 VIII 2440
- 913. 猫和老鼠 2800
- 1728. 猫和老鼠 II 2849
- 294. 翻转游戏 II(会员题)
十五、概率 DP、期望 DP
- 688. 骑士在棋盘上的概率
- 837. 新 21 点 2350
- 1467. 两个盒子中球的颜色数相同的概率 2357
- 808. 分汤 2397
- LCR 185. 统计结果概率
- 九坤-04. 筹码游戏
- 1230. 抛掷硬币(会员题)
专题:输出具体方案(打印方案)
注意这些题目和回溯的区别,某些回溯题目要求输出所有方案,这里只要求输出一个。
- 368. 最大整除子集
- 1363. 形成三的最大倍数 1823 字典序
- 1449. 数位成本和为目标值的最大数字 1927 字典序
- 1092. 最短公共超序列 1977
- 943. 最短超级串 2186
- 1125. 最小的必要团队 2251
- 3533. 判断连接可整除性 2257 字典序 暴搜
- 3260. 找出最大的 N 位 K 回文数 2370 字典序 暴搜
- 3149. 找出分数最低的排列 2642 字典序
- 3441. 变成好标题的最少代价 2765 字典序
- 3348. 最小可整除数位乘积 II 3101 字典序 暴搜
- 656. 金币路径(会员题)字典序
- 471. 编码最短长度的字符串(会员题)
专题:前后缀分解
部分题目也可以用状态机 DP 或者分组循环(见双指针题单)解决。
如果涉及到的只是若干元素,而不是前缀/后缀这样的一段元素,也可以用「枚举右,维护左」思考,见数据结构题单。
- 724. 寻找数组的中心下标 做到
- 1991. 找到数组的中间位置 同 724 题
- 2574. 左右元素和的差值 1206
- 3707. 相等子字符串分数 1262
- 3912. 数组中的有效元素 1273
- 3788. 分割的最大得分 1306
- 2270. 分割数组的方案数 1334
- 3904. 最小稳定下标 II 1352
- 2256. 最小平均差 1395
- 1422. 分割字符串的最大得分 做到
- 238. 除了自身以外数组的乘积
- 1493. 删掉一个元素以后全为 1 的最长子数组 1423
- 334. 递增的三元子序列
- 845. 数组中的最长山脉 1437 也可以分组循环
- 2012. 数组美丽值求和 1468
- 2909. 元素和最小的山形三元组 II 1479
- 2483. 商店的最少代价 1495
- 1525. 字符串的好分割数目 1500
- 3583. 统计特殊三元组 1510
- 3354. 使数组元素等于零 做到
- 1930. 长度为 3 的不同回文子序列 1533
- 2780. 合法分割的最小下标 1550
- 2874. 有序三元组中的最大值 II 1583
- 1664. 生成平衡数组的方案数 1590
- 926. 将字符串翻转到单调递增 1602
- 3698. 分割数组得到最小绝对差 1649
- 3598. 相邻字符串之间的最长公共前缀 1655
- 2222. 选择建筑的方案数 1657
- 1031. 两个无重叠子数组的最大和 1680
- 689. 三个无重叠子数组的最大和
- 2420. 找到所有好下标 1695
- 3862. 找出最小平衡下标 1697
- 123. 买卖股票的最佳时机 III 拆分成两个 121 题
- 2100. 适合野炊的日子 1702
- 3983. 一次替换后的子序列 1755 子序列匹配
- 1653. 使字符串平衡的最少删除次数 1794
- 1186. 删除一次得到子数组最大和 1799
- 3738. 替换至多一个元素后最长非递减子数组 1811
- 42. 接雨水(讲解)
- 2711. 对角线上不同值的数量差 做到
- 1477. 找两个和为目标值且不重叠的子数组 1851
- 2680. 最大或值 1912
- 1671. 得到山形数组的最少删除次数 1913
- 3891. 最大化特殊下标数目的最少增加次数 1953
- 3872. 替换最多一个元素后的最长等差子数组 2042
- 2906. 构造乘积矩阵 2075
- 3334. 数组的最大因子得分 非暴力做法
- 3830. 移除至多一个元素后的最长交替子数组 2162
- 2167. 移除所有载有违禁货物车厢所需的最少时间 2219
- 2484. 统计回文子序列数目 2223
- 2163. 删除元素后和的最小差值 2225
- 2565. 最少得分子序列 2432
- 1995. 统计特殊四元组 四个数
- 2552. 统计上升四元组 2433 四个数
- 3302. 字典序最小的合法序列 2474 第 2565 题的进阶版本
- 3404. 统计特殊子序列的数目 2445 四个数
- 3303. 第一个几乎相等子字符串的下标 2509
- 3287. 求出数组中最大序列值 2545
- 3257. 放三个车的价值之和最大 II 2553
- 3410. 删除所有值为某个元素后的最大子数组和 2844
- 3003. 执行操作后的最大分割数量 3039
- 487. 最大连续 1 的个数 II(会员题)
- 1746. 经过一次操作后的最大子数组和(会员题)
专题:跳跃游戏
- 1306. 跳跃游戏 III 1397
- 2770. 达到末尾下标所需的最大跳跃次数 1533
- 403. 青蛙过河
- 1340. 跳跃游戏 V 1866
- 1871. 跳跃游戏 VII 1896
- 1696. 跳跃游戏 VI 1954
- 975. 奇偶跳 2079
- 1654. 到家的最少跳跃次数 2124
- LCP 09. 最小跳跃次数
- LCP 20. 快速公交
- 656. 金币路径(会员题)
- 2297. 跳跃游戏 VIII(会员题)
其他
- 1387. 将整数按权重排序 1507
- 397. 整数替换
- 2998. 使 X 和 Y 相等的最少操作次数 1795
- 823. 带因子的二叉树 1900
- 1553. 吃掉 N 个橘子的最少天数 2048
- 2930. 重新排列后包含指定子字符串的字符串数目 2227
- 1896. 反转表达式值的最少操作次数 2532
算法题单
- 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
- 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
- 单调栈(基础/矩形面积/贡献法/最小字典序)
- 网格图(DFS/BFS/综合应用)
- 位运算(基础/性质/拆位/试填/恒等式/思维)
- 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
- 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
- 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
- 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
- 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
- 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
- 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)
欢迎关注 B站@灵茶山艾府
如果你发现有题目可以补充进来,欢迎评论反馈。


