DP笔记 2021.2.4 下午
Preview
今下午的主要内容是
-
序列 DP
-
区间 DP
可以说是动态规划里面最简单的一种类型, 通过学习和思考此类问题, 大家能够加深对动态规划本身的理解.
序列 DP
序列上的 DP 状态设计最基本的形式
-
表示以 结尾的最优值或方案数
-
表示以 结尾附加信息为 的最优值或方案数, 转移的话往往是枚举上一个断点.
( 是一个满足转移条件的断点)
这是最简单的一类序列上的 DP
Luogu P1772 [ZJOI2006]物流运输
有 个码头和 条航线, 每条航线有成本. 有连续 天需要从 号码头到 号码头运输货物. 每个码头会在某些天数区间内不许经过. 每更换一次运输路线, 要付出 的成本求这 天的最小总成本.
,
其实就是分成很多段, 每一段选同一个运输路线, 然后得到一个最优的划分方案, 使得成本最小.
表示前 天的运输最小成本。
其中 表示最短的在第 天到第 天都能用的路线长度, 把能在则几天一直走的点加进图中, 跑最短路径即可.
bzoj1296粉刷匠
有 条木板要被粉刷, 每条木板分为 个格子, 每个格子需要被刷成蓝色或红色.
每次粉刷可以在一条木板上给连续的一段格子刷上相同的颜色. 每个格子最多被刷一次.
问若只能刷 次, 最多正确粉刷多少格子.
,
如果只有一条木板, 那么设 表示前 个格子刷 次的最多正确格子
为第 到第 个格子的最多同色格子数, 哪个颜色出现的多刷哪个, 直接记一个前缀和即可.
有多条木板, 设 表示前 个木板刷 次的最大答案.
其实像这种般的 DP, 就是把影响答案的信息用多维状态来表示, 什么必要什么就放在状态里.
CF 314E
给定一个长度为 的仅包含左括号和问号的字符串, 将问号变成左括号或右括号使得该括号序列合法, 求方案总数. 例如 与 都是合法的括号序列.
括号序列问题, 往往就是把左括号看成 , 右括号看成 ,我们只需要保证任意一个前缀大于等于 , 且总和为 , 就代表是个合法括号序列了.
令 表示当前到第 个字符, 现在还有 个左括号.
那么分 种情况考虑:
-
若第 个字符是左括号, 则能转移到 .
-
若第 个字符是右括号, 则能转移到 .
-
若第 个字符是问号, 则能转移到 与 .
最终 就是方案总数啦.
bzoj4922
给出一些括号序列,要求选择一些括号序列拼接成一个合法的括号序列, 使得总长最大.
, 表示括号序列的个数
括号序列的长度 不超过 .
首先对于每个括号序列, 把左边的左括号和右边的右括号对消, 最后能得到一坨这样的东西:
就是 个右括号然后 个左括号, 记作
然后考虑假如我们的子集选好了, 我们要按照什么顺序拼接才能拼成一个合法的括号序列呢?
这就转化成了另一个问题:
BZOJ3709
在一款电脑游戏中, 你需要打败 只怪物 (从 到 编号). 为了打败第 只怪物, 你需要消耗 点生命值, 但怪物死后会掉落血药, 使你恢复 点生命值. 任何时候你的生命值都不能降到 (或 以下)
请问是否存在一种打怪顺序, 使得你可以打完这 只怪物而不死掉.
贪心, NOIp 的贪心很多都是按照某种方式排序, 然后依次选或处理.
我们来看看应该怎么排序.
-
如果 , 说明打掉这个怪兽有血可恢复, 那么血量会变多, 明显我们按照伤害 从小到大排序即可, 然后一个个杀下来.
-
如果 , 说明会亏血. 一个精妙的想法就是, 最后剩余的血量值, 假设是 , 那么 是固定的. 然后可以看作初始血量为 , 怪兽的属性 , 交换,这样就和上一种情况一样了.
回到bzoj4922
我们还是把左括号看成 , 右括号看成 , 同样是保证任意一个前缀大于等于 , 且总和为 .
那就是每一个给定的序列都是 先 再 , 是对消后左端右括号的数量, 是对消后右端左括号的数量. 然后依次拼起来之后任何一个前缀都大于等于 , 这个其实和刚刚所讲的题目完全一样.
我们按照上一题的做法排序即可, 排序后我们从左往右做 DP.
设 为前 个括号序列 与 的和为 个时选出括号序列最长的长度和.
也就是前 个括号序列左括号比右括号多 个时的最长的长度和.
转移时考虑下一个括号序列选不选即可.
为排完序后第i个括号序列的长度.
最后答案就是 . 复杂度
卡特兰数
-
依次进栈, 求有多少种可能的出栈序列.
-
由 对括号形成的合法的括号序列由多少个?
-
个节点共能构成多少种二叉树, 左右子树是认为不同.
-
凸多边形的三角划分的方案数: 把一个凸多边形用 条直线连接 对顶点, 共形成n-2个三角形,求方案数.
-
一个 的格子, 从 走到 , 求不跨过 这条直线的路径方案数.
我们设 表示 个数依次进栈所能形成的出栈序列数.
似乎和之前不一样, 好像不是划分成一段一段那样的简单形式.
我们可以考虑另一种形式的状态转移方式, 以转移到子问题.
注意一段一段划分我们可以枚举最后一段的起点, 但是这里不是一段一段的, 我们要考虑另外的转移方式.
实际上我们发现我们可以枚举 这个数是什么时候出栈的. 那么我们可以得到:
其实还有一个更简便的形式:
一个经典题
有 个数, 选择其中若干数, 使得每连续 个数中都至少有一个数被选中, 且选出的数的和最小.
令 表示前 个数满足题目要求且第 个数被选中, 这样的情况下选出的数的和最少是多少.
通过枚举上一个被选出的数 在哪里, 有:
单调队列
我们合法的转移区间不断向右移动, 而这就是一个典型的滑动窗口问题.
对于两个决策 , , 满足
若 , 则当 , 时, 能代替
若 , 则无论何时, 都不可能比 优, 可以直接删除.
每次在队列末尾插入删除一个数或者在队首删除一个数, 且该队列始终保持单调递增.
因此称为单调队列优化.
每个数进入队列出队列一次, 转移是 的, 总时间复杂度为
int l=1,r=0;
for (int i=1;i<=n;i++) {
while (l<=r&&q[l]<i-m)
l++; //把已经不在合法转移区间去掉
dp[i]=a[i]+dp[q[l]] ;
while (l<=r&&dp[l]<=dp[q[r]])
r--; //核心,及时弹出对答案贡献一定不如 i 的元素
q[++r]=i;
}
区间 DP
区间 DP 一般就是设 表示区间 所能形成的最优答案或者方案数.
或者像序列一样, 多加几维表示附加的信息.
最简单的区间dp:合并石子
有 堆石子, 每次只能合并相邻的两堆石子, 并且消耗相邻两堆石子个数的体力值, 问最少消耗多少体力值将 堆石子合并为 堆.
表示将区间 这段区间内的石子合并为 堆的最小体力值.
答案就是 .
转移, 考虑对于区间 它一定是由两段区间 , 合并成的, 所以转移就考虑枚举 区间内的一个分割点 转移即可。
一定要注意区间 DP 的枚举顺序!
for (int l = n; l >= l; l--)
for (int r = l; r <= n; r++)
if (l == r)
dp[l][r] = 0;
else {
dp[l][r] = inf;
for (int k = l; k < r; k++)
dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]+(sum[r] - sum[l - 1]));
}
CF 245H
给定一个字符串 , 组询问, 每次询问区间 内有多少回文子串.
状态: 表示区间 有多少回文子串.
poj 3280
给你长度为 的字符串, 其中有 种字符, 每种字符都有两个值, 分别是插入这个字符的代价, 删除这个字符的代价, 让你求将原先给出的那串字符变成一个回文串的最小代价.
代表区间 到区间 成为回文串的最小代价, 那么对于 有三种情况:
-
表示区间 到区间 已经是回文串了的最小代价, 那么对于 这个字母, 我们有两种操作, 删除与添加, 对应有两种代价, 或 , 取这两种代价的最小值.
-
表示区间 到区间 已经是回文串了的最小代价, 那么对于 这个字母,同样有两种操作, 或 , 取最小值.
-
若是 , 表示区间 到区间 已经是回文串的最小代价, 那么对于这种情况, 我们考虑 与 的大小.
然后 取上面这些情况的最小值.
能量项链
环形问题有一个很常见的处理办法是, 断环为链, 然后把这个链复制一遍接在原链的后面.
然后做区间 DP, 最后取答案就是找 里面取最优的即可.
在项链上有 颗能量珠. 能量珠是颗有头标记与尾标记的珠子, 这些标记对应着某个正整数. 并且, 对于相邻的两颗珠子, 前一颗珠子的尾标记一定等于后一颗珠子的头标记.
如果前一颗能量珠的头标记为 , 尾标记为 , 后一颗能量珠的头标记为 , 尾标记为 , 则聚合后释放的能量为 , 新产生的珠子的头标记为 , 尾标记为 .
需要时, Mars人就用吸盘夹住相邻的两颗珠子, 通过聚合得到能量, 直到项链上只剩下一颗珠子为止. 显然, 不同的聚合顺序得到的总能量是不同的, 请你设计一个聚合顺序, 使一串项链释放出的总能量最大.
在读入的时候现将珠子们复制一遍放到后面, 断环成链
设 表示左端点为 号珠子, 右端点为 号珠子的区间所能得到的最大能量, 转移就枚举最后一步聚合的位置即可.
区间dp两类主要的转移套路
一般 的区间 DP 问题, 由于状态就是二维的了, 转移一般都是 , DP 转移过程中主要考虑就是 和 两个边界的情况, 正如之前的 道题.
而 的区间 DP, 除了边界往往还要枚举这个区间从哪个位置划分.
不能说一定是这样, 但是绝对是一个很好的思考方向. 看数据范围猜算法.
总结
-
组合数学的经典数列: 卡特兰数
-
序列 DP 问题一种常见的优化方法: 单调队列优化. 之后背包 DP 和基环树问题中,都要涉及单调队列优化, 我们在此先对其有个初步的了解.
-
括号序列问题, 把左括号看成 右括号看成 的常用套路.
-
区间 DP 状态设计的一般形式.
-
区间 DP 处理环形问题.
-
区间 DP 转移一般考虑区间边界的情况, 或者由两个小区间合起来的情况.
整理自赵和旭的 DP 课件.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具