dp优化与套路

环形处理

形式成环

最基础的就是破环成链,对于区间 \(dp\) 非常方便

在这个基础上,对于形如“对相邻两点的操作”类题,可以发现一定有一个分界点是没有被操作过的,如果都操作过那么相当于顺着环走了一遍
这个分界点可以通过二分、\(dp\) 求得,同时可以寻找单调性移动


C. 寿司

可以发现一定是一种颜色推到两边,剩下的换到中间
可以利用单调性指针维护中间端点位置


B. 钱仓

可以贪心地去找断点位置,对于每个点 \(-1\),然后找到最大子段和
可以拓展一下从而和二分结合


也可以处理出拼接点两端的所有信息,再依次合并


P6064 [USACO05JAN]Naptime G

\(f[i][j][0/1]\) 表示前 \(i\) 个时间睡 \(j\) 此当前睡没睡的最大值
但是发现最后一段睡不睡影响着第一段时间睡觉的价值
那么可以进行两次 \(dp\),分别强制第一个睡和不睡
如果强制不睡,\(f[1][0][0]=f[1][1][1]=0\)
如果强制睡,\(f[1][1][1]=a[1],f[1][0][0]=0\),注意这时候最后一个取贡献时只能 \(f[n][m][1]\)

在基环树上与仙人掌上的用途很常见


转移成环

可以将转移过程分成互不影响的层,每层同时转移即可


CF762D Maximum path

由于上下左右都能走一定会形成环
那么就要利用 \(n=3\) 这个美好性质了
可以发现只可能在中间那行往左走,然后从另一行折返回去
那么发现这样会填满一个 \(3* m\) 的格子,那么只需要预处理最大的 \(f[1][i]+totw\) 来转移 \(f[3][j]\) 即可,其他类似


  • 由于最短路算法的完备性,最短路天然地避免了成环带来的影响
    其实,\(dp\) 本质上就是一个图上的最短路或计数等问题,只不过大多是 \(dp\) 都是一个 \(DAG\) 可以直接拓扑

D. Frog Traveler

写出来转移柿子和线段树优化 \(dp\) 式子,然后线段树优化一下,发现 WA 一片
为什么呢?因为转移有环
上下跳的过程中是可以产生环的
那么可以采用 \(bfs\) 的方式来避免,类似于最短路的转移


P3943 星空

这道题的关键在于 \(dp\) 出长度为 \(i\) 的两个灯抵消的代价,这个可以直接最短路(\(bfs\)),其实负体积背包就可以


dp 套 dp

这个说实话其实没有什么特别的,相当于是把一部分相同状态单独拎出来进行预处理 \(dp\)

P4158 [SCOI2009]粉刷匠

比如这道题
很暴力的 \(dp\)\(f[i][j][k]\) 表示刷到第 \(i\) 块,刷了 \(j\) 次,当前刷到第 \(k\) 个位置
转移时需要枚举上一块,这一次刷了多少,上一块刷的位置等等,复杂度较高
不妨把每一块先单独拎出来 \(dp\) 一遍
\(g[i][j][k]\) 表示第 \(i\) 块,刷了 \(j\) 次,刷到 \(k\) 位置,转移只在层内进行
那么 \(f\) 简化为 \(f[i][j]\) 表示前 \(i\) 个木板刷了 \(j\) 次,转移只在层间进行,运用 \(g\) 当做价值


好吧,其实正宗的 \(dp\)\(dp\) 并不是干这个的

在有些情形下,方案的统计分为两层,比如最大值的方案,或可行性的方案,或方案的方案等等,其中对于一种固定的外层的方案,内层的情况可以通过 \(dp\) 求出,并且可行 \(dp\) 情形不太多,那么只需要把内层的所有 \(dp\) 值当成状态放到外层再次进行方案统计即可


bzoj3864 Hero meet devil

首先内层的 \(dp\) 是一个经典的 \(LCS\)

考虑对于同层的 \(i\),所有的 \(j\) 位置的状态数有多少

可以发现根据表达式的特性,\(dp\) 值每次最多会增加 \(1\),那么状态数最多为 \(2^{|S|}\)

然后对于一个固定的 \(m\) 串的某一位的选择,这个 \(dp\) 值集合的状态转移到的 \(dp\) 值集合的状态是固定,那么可以把这个先预处理出来,外层直接进行转移即可


hdu5079 Square

题意:对于给定的部分禁止的棋盘黑白染色,对于每个 \(i\) 求出多少种染色方案使得最大白色正方形边长为 \(i\)

首先考虑一种比较暴躁的做法:

首先一行一行进行 \(dp\),可以发现每一个位置需要记录这个位置向上有多少个白色块才可以表示出所有可能的正方形。

一个非常神奇的优化方式是首先枚举 \(len\),可以发现此时每行只需要记录 \(n-len+1\) 个位置的状态即可,且只需要记录最多 \(len\) 的高度,那么状态数为 \(\sum len^{n-len+1}\) 可以接受

注意这个做法其实提供了理论支持,证明了内层 \(dp\) 的状态数不多!

发现对于一个固定的外层状态,内层是需要 \(dp\)

转移为 \(f[i][j]=min(f[i-1][j],f[i][j-1],f[i-1][j-1])+1(白格),f[i][j]=0(黑格)\)

验证得到这个的状态可以接受,总共只有 \(3000\)

另外全局的 \(max\) 需要额外开个状态进行记录,然后就可以暴力转移了


String Path

题意:统计有多少 \(n\times m\) 的字母矩阵,使得从左上角右下移动时可以得到两个模式串各一次

由于只能往下和往右移动,那么可以采用对角线上的 \(dp\),这样可以拥有严格的状态划分

考虑这个东西是不能直接设出位置信息的,显然转移是会重复

因为其实对于一种最终的方案,内层的路径条数也是一种 \(dp\),如果直接计算将会把这个 \(dp\) 默认为方案数 \(dp\),现再我们要将其强制转化为可行性 \(dp\)

那么我们设 \(f[i][S_1][S_2]\) 表示第一个字母边界在 \(S_1\) 可行且第二个字母在 \(S_2\) 可行的填字母的方案数,这样就可以解决上述问题了


Jigsaw Puzzle

可以发现一样是刚才的问题,暴力计算会算成摆放牌的方案数

考虑此事内层的 \(dp\) 状态是边界为 \(S\) 是否可行,可以发现可以发现有许多同等类型的状态,比如相邻两个横的牌转化为竖的牌一定不劣,可以发现搜索后这样的方案只有 \(21\)

继续对所有转移进行搜索,发现可以被到达的状态最多只有 \(60\) 种,于是就可以愉快地进行 \(dp\)

posted @ 2022-08-26 19:55  y_cx  阅读(59)  评论(0编辑  收藏  举报