笔记 dp套dp

 


笔记 dp套dp

本篇侧重 理解

从本质入手

dp的本质: 我们有一个要解决的问题,把问题分成若干步(dp的阶段), 每一步都有一个状态表示(dp的状态),然后考虑状态间的转移。dp把问题通过某种顺序解决,使得我们可以 不记录 一些东西,而只记录我们关心的东西,以压缩我们描述状态用的东西

简单的说,就是我们把研究的问题的特征打包变成一个节点,节点连成DAG(通常),然后我们在DAG上拓扑的跑,就是dp的转移。

进一步思考

现在有一个能dp的问题,给定一个答案,求有多少种问题,使得输入它能得到给定的答案。

答案不一定是一个简单的数

首先我们必做的一件事情是,把这个问题的dp搞出来 (这个不搞后面咋搞)

接下来,能得到“答案”,还能如何变形?可以想到,把它变成能得到(内层dp的某一些) “状态”/“节点”

一拍脑袋觉得很有道理,接下来我们就有了设计外层dp的大方向:我们用和内层dp类似的方式拆解原问题,但是在每个阶段里,我们不去求答案,而是算有多少种输入能使得内层dp的值,长成某个我们想要的样子。

那外层dp的大概样子就是 f(U,P) 表示,内层dp考虑到 U 节点,我们要研究的(若干个)内层dp值(打个包)是 P ,的方案数。

为啥要考虑内层dp的节点?

  • 因为我们和内层dp一样拆问题,就比如序列问题,内层dp是一位一位考虑,那我们外层的dp也一位一位的考虑,这时候我们的状态里就会和内层dp一样,有一个当前位置 i

为啥我们要可能研究不止一个内层dp值?如何打包?

  • 这个看内层dp的转移,如果要用到很多前驱dp值,那我们可能就要把一些dp值,比如说一行的dp值,给打包一下
  • 打包方法最常见的就状压吧,其它的方法和状压也差不多,比如可能会有三进制状压之类的

检验真理

来点实践。

bzoj3864 Hero meet devil

给定一个串 S(|S|15),和一个 m。对于 k=0...|S|,要求有多少串 T 使得 |T|=mLCS(S,T)=k。(这里LCS是子序列)

考虑LCS的dp,f(i,j) 表示 LCS(T[:i],S[:j])。这个显然可以转移,但是我们发现更多性质,就是 f(i,j+1)f(i,j),但是 f(i,j+1)f(i,j)+1。这两个都很显然,但是这代表 f(i,) 的差分数列就是个 0/1 数列。

那我们就可以压了(注意到 |S|15,显然可以压)。根据上面那一套思想,我们外层 dp 就这么设计:

f(i,M) 表示 Ti,这一行的内层dp数组做差分后压缩一下为 M,的方案数。

你可能会问,原dp中的j哪去了。 注意到我们直接把一整行打包了,所以这个j就没了,并且我们转移的时候是直接一整行转移的。

接下来我们考虑 Ti+1,加了某个字符。但是加上字符的变化,可以推一下,和 i 并没有关系,只和这一行的 dp 状态 M 有关。那我们可以很容易的从 f(i,M) 转移到 f(i+1,M),最后随便统计一下就得到答案了。

TopCoder StringPath

给定两个长为 n+m1 的串 A,B,问有多少个 n×m 的字母矩阵,使得它恰好存在两条从左上到右下的路,一条的字符连起来是 A,另一条是 B

同样用类似的套路,先考虑内层dp。

内层的dp是用一个二维的 dp[i][j][2] 记录到 (i,j) ,是否能匹配 A/B 的前 i+j1 位。

接下来考虑外层dp。就是考虑有多少个字符矩阵能到达某个内层dp的状态。内层dp的值我们发现它就是两个0/1,果断状压。

然后我们和内层一样,按枚举坐标 (i,j) 考虑,并想怎么转移到 (i,j+1)。我们发现这个东西的转移和左,上有关,并且还要可延续(即不能只记录它的左,上),所以我们压0/1压的是轮廓线,一条轮廓线存 m 个,而因为有 A,B 两个串,所以要存两条。

这里有个小细节,这个轮廓线应该是包含 (i,j),即 (i,j+1) 头上的那条轮廓线,如下图,如果红色表示 (i,j),那我们记录的轮廓线是蓝色这一带。

image-20210629220638864.png

实现的时候,我们直接拿状压的bitmask的(从低到高)第i位,表示轮廓线从左到右的第 i 个,而不是从上面开始数,一直顺序记到 (i,j) 为止

也就只有我这种傻逼会想到后者这种阴间记法吧

这样我们就有了外层dp的状态,dp[i][j][A][B] 表示到 (i,j) 位置,内层的dp的值的轮廓线状压起来是 A,B,有多少种。根据内层dp的轮廓线,很方便转移。然后这题就没了,复杂度 O(nm22m)

总结

dp套dp,它就是在原来的dp转移DAG上dp。

尽管我大概懂了它是个什么东西,但是我练的还太少,写题不够熟练

看提交记录里面一堆WA就知道了

posted @   Flandre-Zhu  阅读(301)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示