NOIP 冲刺之——dp
前言
本篇文章主要记录笔者 NOIP 冲刺阶段复习的各种 dp 题型及 tricks ans tips,同时也用于及时复习与巩固。
那么,开始吧。
线性 dp
线性 dp 对我来说是一类很捉摸不定的题型:她太综合了,可以和任何知识点合起来考,这里就先抛开“数据结构优化”的成分展开来讲。
线性 dp 的状态设计不一定都是一维的,是指在状态转移时在每一维上是线性转移。就比如:
这种式子就是线性 dp 的大概模样。
如果很幸运,你看出一道题有线性 dp 的做法,那么可以从几个方向入手:
- 观察数据范围,这通常可以启发状态设计;
- 注意题目中具有明显线性转移的语句;
然后根据设计出的状态,通常只需根据转移方程依次循环每个维度即可求解。
还需要抓住主要矛盾:找出 dp 的阶段,然后把它丢到最外层循环去。
接下来来看几道例题:
Frog 1
首先发现青蛙只能跳一格或两格,所以可以设
Vacation
一共有
状态转移方程:
答案就是第
其实这道题也是状态机模型 dp 的一道经典题目。
Coins
表面上是概率 dp,但不要被唬住了,本质上还是一道简单的线性 dp。
通过题目描述(主要)和数据范围(次要)可以大概确定状态是二维的,
对于第
顺便地,线性 dp 有一个优化空间的小技巧:滚动数组(其实在其它类型的 dp 中有时也会用到)。
比如这一题,我们发现每次只会用到
加滚动数组优化也很简单,这里我们只需要两层,那么就在所有第一维的地方加上一个 & 1
,就行了。同理,如果需要 & k - 1
。
综上,状态转移方程就变为了:
Candies
挺有意思的一道题。
首先朴素 dp 很好想,根据数据范围大概可以猜出状态设计:
那么状态转移方程为:
但是这样转移时间复杂度是
这里我想了一个十分类似完全背包优化的方法。
发现有很多重复项,所以可以列出相邻的项找关系。
把
dp[i][j] = dp[i-1][j] + dp[i-1][j+1] + dp[i-1][j+2] + ... + dp[i-1][min(j+a[i]-1,m)] + dp[i-1][min(j+a[i],m)]
dp[i][j-1] = dp[i-1][j-1] + dp[i-1][j] + dp[i-1][j+1] + dp[i-1][j+2] + ... + dp[i-1][min(j+a[i]-1,m)]
会发现他们中间有很多项都是完全一样的,所以
for(int i = 1; i <= n; i++)
for(int j = K; ~j; j--)
if(j + a[i] < K) dp[i][j] = ((ll)mod + dp[i][j + 1] + dp[i - 1][j] - dp[i - 1][j + a[i] + 1]) % mod;
else dp[i][j] = (dp[i][j + 1] + dp[i - 1][j]) % mod;
P1541 [NOIP2010 提高组] 乌龟棋
根据数据范围题目描述不难猜到这个题是个多维度的线性 dp,直接来!
设已经使用了
对于每一维都枚举一遍,设
P11233 [CSP-S 2024] 染色
考场上犯糖了,不知道为什么跳了这道题,而且 20pts 的暴力也打挂了,可以说是一败涂地。
其实这道题还挺简单的,不管是暴力还是优化。
很显然的序列上线性 dp,先思考
设
那么状态转移方程就为:
那么就可以
但是很快我们就认识到转移能优化成
为什么呢?
因为
而且为了得到这个贡献也是不择手段——必须钦定区间
令
那么现在就可以将转移优化到
注意到
综上所述,时间复杂度为
要特别注意!此时状态转移方程变成了:
为什么是
P1387 最大正方形
好像是一类比较有意思的题的最基础版本。
设
那么可以看一下右下角的情况:
可以看到,首先
而向左上扩展多少就是
然后这道题的状态转移方程就推出来啦:
P1736 创意吃鱼法
这道题是找最大的只有对角线是
同上一道题的思考方式,但是对角线有两个方向所以分开做。
设
画图来看:
先考虑左边。
对于一个点
那么状态转移方程为
区间 dp
对于这种 dp 我都是类比线段树的合并来理解,一般有很明显的区间性,通常采用枚举分界点的方式来转移。
题目没时间贴了,咕咕咕……(反正考到了简单的能做,难的话也做不来)
树形 dp
和图论有明显的交集,可以借助图论的思想形象地思考问题和划分阶段,所以个人认为在某些方面比线性 dp 和区间 dp 好理解。
知识点 :树上背包
来不及复习啦/qaq。
知识点 :换根 dp
对于这种题我建议先打暴力,然后在暴力的基础上做等价变换。
来回顾一下换根 dp 的基本思路:
- 第一次 dfs,任选一个点为根进行方才的树形 dp;
- 第二次 dfs,从相同的根出发,再扫描一遍从父节点向子结点更新信息,这里多半会用到剔除贡献的问题,要么记最大 / 次大值和具体从哪个点更新(这个主要用于最大 / 最小的不满足可减性的信息),要么从第一遍 dfs 的信息更新处倒推(这个一般用于满足可减性的信息)。
P3478 [POI2008] STA-Station
经典入坑题。
先打暴力,设
状态转移方程为:
接着再换根,考虑父节点怎么更新子节点。
令
综上,
二次扫描即可。
[ABC348E] Minimize Sum of Distances
经典变式题,推了一下发现就是把上一道题的
P10974 Accumulation Degree
蓝书上的例题。
题目传送门
题目大意:
给定一颗无根树,有一个节点是源点,度数为
现在需要求出:在流量不超过最大流量的前提下,选取哪个点作为源点,整个水系的流量最大,输出最大值。
思路:
朴素的想法是枚举某个点作为源点,然后做树形 dp,设
对于每个点都这样做一遍,取
因为这个题是一个无根树,而我们又要枚举根节点,所以不难想到用换根 dp 来代替源点的枚举。
所以考虑用换根 dp 来优化。
对应到这个题上就是思考子节点流向父节点的信息怎么计算。
先画个图:
这里
这道题的信息显然具有可减性。
如图,我们可以考虑先去掉
但是这里有个魔鬼细节,需要考虑节点的度数,因为度数为
那么这道题就结束了,时间复杂度
P11324 【MX-S7-T2】「SMOI-R2」Speaker
今天打的梦熊模拟赛的题,考场上忘了换根 dp 怎么写,所以只用 (话说这道题怎么还不升蓝)
暴力做法:
画个图理解一下:
不难发现每个询问其实就是询问
那么对于每个点都与处理出这样的
时间复杂度
发现时间复杂度瓶颈在于求
这个信息不满足可减性,所以需要用记录记最大 / 次大值和具体从哪个点更新。
记
画图理解一下:
若
为什么还要与
综上,先 dfs 两次求出
其他:
咕咕咕……
数据结构优化 dp
P2569 [SCOI2010] 股票交易
先想暴力,设
将当前的操作分为四种:
- 空仓买入;
- 持股不操作;
- 持股买入;
- 卖出。
第一种操作就是赋初始值:
第二种操作就是继承上一个状态:
第三种操作从背包的角度考虑,设买入了
第四种操作类似,设卖出了
这样是
发现当
将操作
操作
操作
对于操作
P2344 [USACO11FEB] Generic Cow Protests G
先推方程:
直接转移显然是
需要尤其注意!树状数组的下标不能为
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效