DP技巧与DP杂题

DP常用技巧

  1. 增加维数
  2. 交换答案与状态
  3. 可行解转最优解
  4. 删掉本质相同的状态
  5. 对部分状态dp
  6. 遇到转移顺序的困难,考虑记忆化搜索
  7. 遇到转移细节过多的问题,考虑从 ii+1 而不是 i1i
  8. 考虑状态时,先把需要记下来的都记一遍,再考虑优化

DP杂题

CF837D (可行性转最优化)

题目

我们把一个数的 roundness 值定义为它末尾 0 的个数。

给你一个长度为 n 的数列,要求你从中选出 k 个数,使得这些选出的
数的积的 roundness 值最大。

n200

题解

考虑一个很暴力状态,设f[i][j][a][b]表示考虑到第i个数,选了j个,因子25的个数分别是a,b的方案是否存在,答案就是对f[i][k][a][b]=true的状态取min(a,b)

我们发现直接这样转移的复杂度是O(n4)的,无法通过本题。

所以我们考虑套路可行解转最优解,设f[i][j][a]表示考虑到第i个数,选了j个,因子2个数为a时因子5最多是多少

这样子复杂度就降为了O(n3),可以通过本题

CF677D (分层图dp)

题目

有一个 n×mn×m 的矩阵 a(1ai,jp) ,求从起点 (1,1) 出发依次遍历值为 1p 的矩阵单元的最短路径曼哈顿距离。保证满足 ai,j=p(i,j) 唯一。

数据范围:1n,m300,1pnm
题解

我们先把数按照种类分组,分成 p 组。

f[i][j] 表示到达目前这个数的最短路,那么转移方程为 f[i][j]=min{f[x][y]+|xi|+|yj|},其中 (x,y) 为上一组中所有数的坐标。

然后要是直接暴力的状态转移的话,是要TLE的,考虑进行优化(下面这个优化真的太神仙了)。

考虑一个界限 K,假设当前组为 T[i],上一组为 T[i1]

那么当 T[i].sizeK 时,我们就用继续用上面的暴力动态转移,那么对于所有的“上一组”点数加起来不会差过 nm,因此总时间复杂度 O(Knm)

如果 T[i].size>K,我们在网格上进行多源点的优先队列BFS,源点是所有的 T[i1] 组内的点,搜出到所有 T[i] 组内的点的最短距离,这样BFS最多跑一遍所有网格,时间复杂度 O(nmlog(nm));由于这样的组数目不会超过 nmK 个,所以总时间复杂度为 O(nmKnmlog(nm))

这样一来,两种加起来的总时间复杂度就是 O(Knm+nmKnmlog(nm))=O(nm(K+nmlog(nm)K)),由此可知取 K=nmlog(nm) 时,时间复杂度最小,为 O(nmnmlog(nm))

P1004 [NOIP2000 提高组] 方格取数(去除冗余状态)

题意

给定一个 n×m(n,m200) 的矩阵,每个格子里有不同的正整数。一个人从左上角走到右下角,走两次,取走两次路径上的数(同一个数不可以重复取),最大化这些数的和

题解

显然我们可以设 f[i][j][k][l] 表示第一次走到 (i,j) ,第二次走到 (k,l) 时的最大值

考虑如何优化

首先,两次走的总步数一定是 n+m 的,同理,如果我们控制两次同时走的话,那么有 i+j=k+l ,这样我们就可以设 s=i+j 省下一维,转移方程很显然,注意两次相遇的问题即可

BZOJ 1801 中国象棋(DP) (去除不必要的维度)

题目

n×m的棋盘上放若干炮使得不互相攻击。有多少种放法?

说人话就是,不存在三炮共线的情况有多少种

n,m100

题解

首先,一个显然的想法是进行状压dp,设f[i][s]表示考虑到第i行,s是一个三进制状态,存每一列都用了多少个炮,这样转移的复杂度是3的指数级的,显然无法通过

我们考虑一件事,就是假设前i行的炮都放完了,那么第i+1行的炮能放的方案数只与剩余了多少位置能放有关,我们并不需要知道这些炮具体放在了什么位置

所以我们可以设f[i][j][k]表示,考虑完前i行,有j列还可以放1个炮,k列还可以放2个炮的总方案数

我们考虑转移

  • 放0个炮,那么就是:

  f[i+1][j][k]f[i][j][k];

  • 放1个,可以放在还可以放1个炮的列中,也可以放在还可以放2个炮的列中:

if(j>=1) f[i+1][j1][k]f[i][j][k]j  if(k>=1) f[i+1][j+1][k1]f[i][j][k]k

  • 放2个,可以都放在还可以放1个炮的列中,也可以都放在还可以放2个炮的列中,也可以每边放一个:

if(j>=2)f[i+1][j2][k]f[i][j][k](j(j1)/2)  if(k>=2)f[i+1][j+2][k2]f[i][j][k](k(k1)/2)  if(j>=1&k>=1)f[i+1][j][k1]f[i][j][k]jk

[SCOI2008]着色方案 (利用题目性质优化状态)

题目

n 个木块排成一行,从左到右依次编号为 1n

你有 k 种颜色的油漆,第 i 种颜色的油漆足够涂 ci 个木块。
且保证 i=1kci=n

统计任意两个相邻木块颜色不同的着色方案

题解

一个变态朴素的想法,我们进行15+2维的dp,那15维状态存对应的颜色剩余的个数,但是显然515实在无法令人接受

我们意识到一个问题,使相邻木块颜色不同并不需要枚举每一种颜色,事实上,我们只要保证当前颜色不与上一次的颜色相同,那么就是合法的转移,所以我们考虑优化状态

f[i][j][a][b][c][d][e]表示考虑到第i位,上一次选的数还剩j次可选,剩余1,2,3,4,5次的数分别还有a,b,c,d,e个,我们上一次选的这时候我们的状态数就为155,这比上面那个东西小多了,复杂度是正确的

我们不能选的状态,就是剩余次数为j的颜色其中一个,直接转移就行

[AGC044A] Pay to Win (记忆化搜索,大力出奇迹)

题目

你需要通过如下操作把 0 变成 N (N1018)

  • A 个金币把数字乘 2
  • B 个金币把数字乘 3
  • C 个金币把数字乘 5
  • D 个金币把数字加一或减一。

求最少需要花费的金币。

题解

倒着记忆化搜索,乘法变成除法,遇到除不尽的通过加法调整

这样形成的就是一个深度为 logN 的六叉树

时间复杂度 O(能过)

[USACO2.3]奶牛家谱 Cow Pedigrees (恰好到不超过)

题目

一个有 n 个节点,深度为 k 的无标号完满二叉树(即每个节点的儿子数为 02)有多少种结构?定义根节点深度为 1

答案对 9901 取模。

3n<2002k<100

题解

考虑到 恰好k个 并不好做,所以我们考虑 不超过k个 怎么做

f[i][j] 表示点数为 i ,最大深度不超过 j 的二叉树的个数

枚举其左儿子大小进行转移即可

复杂度 O(n2k)

ZR2022 NOIP十连测Day6 T1 跳棋(有向图+tarjan缩点+拓扑排序/记忆化搜索的一类经典套路)

sb出题人卡常数,下面这个做法过不了

题目

在一个无穷大的跳棋棋盘上,放有 n 个棋子,第 i 个棋子的坐标为 (xi,yi) ,问当前局面下各个棋子分别能够到达多少个位置

题解

考虑将每个棋子的答案分成两部分

  • 周围六格中没有放棋子的部分

    map 记一下各个棋子的位置,然后枚举就行, O(n)

  • 连续跳跃的部分

    首先连续跳能够跳到的格子一定不超过 6n 个,因此能跳的位置连上有向边,边数也是 O(n) 级别的

    然后用 tarjan 给有向图缩点,跑拓扑排序或者记忆化搜索得到答案

P7516 [省选联考 2021 A/B 卷] 图函数 (Floyd最短路dp)

PS:因为最短路算法和dp有着千丝万缕的联系,所以我在这里放几道偏图论的题应该问题不大(逃

题目

题解

第一步,拆贡献,我们直接计算每个点对 h(G) 的贡献

我们考虑一个问题,两个点可以相互到达,就说明了这两个点不在同一个强连通分量中,那么对于点 u ,我们考虑完前 x1 个点之后,后面的图上的点都没有办法走到这 x1 个点,因为前 x1 个点已然不在强连通分量中,没有必要管了。

那么现在问题转化为,对于前 n 个图 G1Gn 分别求出有多少个点对 (u,v),uv 满足存在不经过 <v 的点使得两点可以互达

一个点对会对答案的前缀产生贡献,为了使答案最大化,我们贪心地考虑每个点对都通过一条使得其最小编号点最大的路径,差分一下处理即可

那么怎么求解两点间经过的最小编号点呢?仔细一想,这很符合 Floyd 算法的转移过程,所以使用 Floyd 求解即可

时间复杂度 O(n3+m) 勉强能冲

P3953 [NOIP2017 提高组] 逛公园 (分层图和spfa最短路dp)

题意

给你一张 n 个点 m 条边的有向图,起点为 1 ,终点为 n ,边有非负边权(可以为 0 )。求解使得 dis(1,n)min{dis}(1,n)+K 的路径数

n1×105,m2×105,K50

题解

神仙题!

由于 k 值很小,所以考虑建立分层图,设 f[i][j] 表示 dis(1,i)=min{dis}(1,i)+j 的方案数,对于一条边 (u,v,w) 转移为

f[u][j]f[v][disu+j+wdisv]

其中 disu 代表的是从 1u 的最短路

但是,我们发现这个转移在 0 环的情况下是错误的,若 0 环出现在最短路图上,则直接导致无解。于是考虑一件事,最短路图在无 0 环的情况下是个 DAG ,因此我们可以对最短路图进行拓扑排序判环,有环则当前层无解,否则拿记忆化搜索直接跑(不用考虑转移顺序多是一件美事),时间复杂度 O(mk)

P7077 [CSP-S2020] 函数调用(DAG建模+dp)

题目

题解

首先考虑一个性质,如果只存在 1 操作,那么直接做就行,所以考虑如何将 2,3 两个操作转化成 1 操作

关键点在于:

  1. 某个函数被调用后序列被乘k,等价于这个函数被执行k次
  2. 乘法可以使用乘法标记

然后拓扑排序dp处理处理即可

P7962 [NOIP2021] 方差 (差分套路+dp)

题目

给定长度为 n 的非严格递增正整数数列 1a1a2an。每次可以进行的操作是:任意选择一个正整数 1<i<n,将 ai 变为 ai1+ai+1ai。求在若干次操作之后,该数列的方差最小值是多少。请输出最小值乘以 n2 的结果

其中方差的定义为:数列中每个数与平均值的差的平方的平均值。更形式化地说,方差的定义为 D=1ni=1n(aia¯)2,其中 a¯=1ni=1nai

1n1041ai600

题解

初看这个操作感觉很无厘头是吧?那么试着把它差分吧!

于是我们发现操作等价于交换原数列差分数组中的两个数

然后我们不喜欢那个丑到爆的方差定义,于是设 S=i=1nai ,转移就是

ni=1n(ai1nS)2=ni=1nai2(i=1nai)2

还有给定的 ai 是单调递增的,所以差分序列是恒大于 0

考虑如何得到最优答案,感性理解一下,答案序列应该是个单谷序列,证明可以考虑调整法,这里不赘述

先将 ai 按差分数组排序,然后设 f[i][s] 表示考虑了前 i 个数,ai=s 时最小的 ai2 是多少,分类讨论当前数应该放在前面还是后面,于是有

f[i1][s]+(j=1ibj)2f[i][s+j=1ibj]f[i1][s]+i×bi2+2bi×sf[i][s+bi×i]

时间复杂度 O(n2a)

P7961 [NOIP2021] 数列 (跟二进制进位有关的dp)

题面

给定整数 n,m,k,和一个长度为 m+1 的正整数数组 v0,v1,,vm

对于一个长度为 n,下标从 1 开始且每个元素均不超过 m 的非负整数序列 {ai},我们定义它的权值为 va1×va2××van

当这样的序列 {ai} 满足整数 S=2a1+2a2++2an 的二进制表示中 1 的个数不超过 k 时,我们认为 {ai} 是一个合法序列。

计算所有合法序列 {ai} 的权值和

1kn300m1001vi<998244353

题解

由于有进位的情况出现,因此考虑从低位向高位dp

f[i][j][k][l] 表示考虑到第 i 位,确定了 a 序列中的前 j 个元素,有 k1,到下一位有 p 的进位,考虑给 S 的第 i 位贡献了 p 位,那么转移就为

f[i+1][j+p][k+(p+l)%2][l+p2]f[i][j][k][l]×vip×(njp)

时间复杂度 O(n4m)

P6758 [BalticOI2013] Vim(线头dp)

原作者题解,这里引用一下LOJ2687 BalticOI2013 Vim 线头DP,因为原作者写的实在是太好了,所以这里建议去原作者博客获得更好的阅读体验,放这里是方便笔者自阅

题目

题解

原题等价于:给出一个序列和两种移动方式,移动过程中必须要经过某一些点,求最小代价。

把连续的f操作和连续的h操作看成线,那么移动路线如下

首先,考虑下面两种移动路线

显然A路线一定没有B路线优

上面的条件等价于对于某一个位置 i,经过的位置覆盖了位置 ii+1 之间的线段的线的数量要么是 1 ,要么是 3 ,对应下图的 AB 两种情况。

pi,j 表示覆盖了 ii+1 之间的线段 1 次,且 f 操作选择字符 j 的最小代价,qi,j,k 表示覆盖了 ii+1 之间的线段 3 次,且在进行 h 操作之前的 f 操作选择的字符是 j、在进行 h 操作之后的 f 操作选择的字符是 k 的最小代价

又设 si 表示字符串的第 i 个字符,impi 表示原串中第 i 个字符前是否存在字符 e

转移:

pi,j=pi1,jjsi&&impi1pi1,si+2qi1,si,jjsiqi1,si,si+2

pi,j 的转移分别对应下图的ABCD情况

其中虚线表示新加入的线,红色字表示对应位置的字符类型,黑色字表示位置编号

qi,j,k=pi1,j+3jsipi1,si+5qi1,j,k+1jsi&&ksiqi1,si,k+3ksiqi1,j,si+3jsiqi1,si,si+5

qi,j,k 转移分别对应下图中的ABCDEF情况






转移就是把线延长和补全的过程,所以叫做线头DP

代码源省选班 字符串 (子段切分问题)

题面

给定一个长度为 n 的由 AB? 组成的字符串,求有多少种方式可以把每个 ? 替换为 A 或 B 后,字符串中,长度为 m 的连续全 A 或全 B 字串恰好有 k 个

题解

一种朴素的状态是: 设 f[i][j][p][c] 表示考虑到第 i 个数,当前有 j 个合法子串,已经有 p 个连续的字符 c 时的方案数,转移是 O(n3)

考虑优化,去掉 p 一维,每次不再填一个字符而是填一个极长连续段,这是这种子段切分问题的一个常见优化思路

那么就有转移

f[i][j][c]=t=1lenf[it][jmax(0,kt+1)][c]

其中 len 表示以 i 为端点不包含 c 的子串长

二维前缀和优化即可

时间复杂度 O(n2)

ZR2020 提高组十连测 day5 T3 (点边数差异不大时的一种处理图上dp的做法)

题意

过了一阵子的辉夜又开始想起了这件事,她开始在意起如果那时候她真的出去乱走,到底什么时候能回到这里呢?可以把街区抽象为一个连通的无向简单图 G=(V,E),这个楼梯在 1 号点,每到一个点 i ,辉夜都要休息 wi 分钟(包括在 1 号点出发之前),多次到达一个点的话需要多次休息。离开一个点的时候,迷茫的辉夜会等概率选择任意一条与这个点相连的边走过去(假定走路不需要时间)。现在辉夜得到了地图,她想知道从楼梯( 1 号点)出发,第一次回到这里时的期望花费的时间(注意这包含了出发前的休息时间,但回到 1 号点后的休息时间不计入),你能告诉她吗?

题解

n,m500 是图上随机游走的板子,高斯消元即可,时间复杂度 O(m3)

树的情况就考虑树形dp,设 fi 表示从 i 出发走向 i 的子树,第一次走到 i 父亲的期望时间,转移就考虑它走到它儿子的概率,方程如下

fu=wu+1duvsonufv

时间复杂度 O(n+m),至此38pts

注意到 n1mn+500 ,感性理解一下,这表明原题中真正需要用到高斯消元的式子并不多。赛时想了一下边双缩点,但这显然没有什么用,出题人给出了一种很震撼的虚树做法

我们考虑先在原图中找出一棵 DFS 树,其中包含了 n1 条边,接着我们依次遍历剩下的 mn+1 条边,把每条边相邻的两个点都在 DFS 树上标记一下,作为关键点;接着,我们根据 DFS 树的连通关系,给我们标记出来的点建一棵树,我们求出这棵树的虚树(实际上是在补全lca,会多出来一些新的关键点),然后将关键点删掉,得到的新图有两个性质

  1. 我们就去掉了原图中所有的环

证明可以考虑反证法,假设删完后存在环,那么至少有一条边环边不在 DFS 树上,环上至少有两个关键点,关键点又被删掉了,出现矛盾

  1. 新图中任意一个连通块至多与两个关键点相邻

同样考虑反证法,假设存在连通块与大于两个关键点相邻,那么一定存在两个端点处的关键点有 lca,这个 lca 也是关键点并且还没有被删(被删了这几个关键点就分开了),出现矛盾

那么我们就可以对每一个连通块做树形dp,加回关键点的时候在虚树上做高消即可算出每个点的答案,时间复杂度 O(m+(mn)3) ,按出题人的意思只能得 77pts

但是我们注意到,加边带来的至多 2(mn) 个关键点再加上建虚树时作为 lca 的关键点 ,我们关键点的个数可以被卡到 3(mn) ,理论上无法通过此题,我不是很看的懂出题人下一步的优化是怎么做的,但是我用上面的做法加fread优化冲过去了

ZR2020 提高组十连测 Day10 T2 (树的拓扑序计数于dp中的应用)

题意

你有一个数列 A=(1),也就是一开始只有一个元素。

你要对这个数列进行 N 次操作,每次操作可以是下面的一种:

  1. A 数列的末尾加入 1 或者 M

  2. 选择一个下标i满足 1i<|A|,然后选择一个数字 x 满足 ai<x<ai+1 或者 ai>x>ai+1,然后将数字插入到 aiai+1 之间。

问有多少种不同的操作序列。这里两个操作序列不同,当且仅当存在某个时刻,使得操作完两个数组的内容不同。

1N3000,2M108

题解

fi 表示考虑到第 i 个操作形成了若干段时有多少种操作序列。如果该位置操作与上一段结尾相同,那么就不能插入别的数;否则就组合数计算方案数,而根据大小关系,我们可以给我们建出来的序列建出一棵树,操作顺序必须是这棵树的一个拓扑序。

而我们知道,有根树的拓扑序数量为

n!u1sizu

因而直接套树的拓扑序计数的套路,每次注意把新生成的子树大小给除上去就行了,时间复杂度 O(n2)

ZR2023 NOIP赛前20连测 Day5 T2 (根据所求缩小状态个数)

题意

小诗想生成一个长为 n 的序列 a,她首先确定了每个位置取值的上下界 [li,ri],接下来对每个位置生成上下界内的任意正整数 ai[li,ri]。她会用以下方式评判 a 的权值:

取某一值域 [1,n3] 的可空子集 S{1,2,,n3},并计算 xSgcd(ax,ax+1,ax+2,ax+3)(在 S= 时定义该式值为 1),并对所有子集的答案求和。

她想知道所有能生成的序列权值之和,由于答案过大,你只需告诉她答案对 109+7 取模后的结果。

对于所有测试点,4n200,1m100

题解

对于一个序列方案,它的权值即为

x=1n3(gcd(ax,ax+1,ax+2,ax+3)+1)

然后考虑 dp ,最暴力的做法就是枚举三个值进行转移,时间复杂度是 O(nm4) 的,无法接受

考虑我们要求的是四个数的 gcd ,所以对于相邻的三个数 a,b,c 我们只需记录 gcd(a,b,c),gcd(b,c),c ,他们三个之间呈倍数关系,因此状态总数来到了 O(mlogm) 级别,总时间复杂度降为 O(nm2logm)

posted on   star_road_xyz  阅读(73)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示