【转载】动态规划超实用套路——刘子沛

前言

本文为二十一世纪二十年代的最新产物作者:刘子沛
微信号:zipeiliu
想转载请随时联系我
本文内容可能过于丰富,建议先扫一遍,然后再分成几个部分每天读一部分,因为这样你才能掌握本文的精髓

干货ahead!!!


动态规划 (DP) 是一个很万能的东西,好比数学中的方程。它也是信奥学习中的重点难点,因此把它学好至关重要。如果你去洛谷等OJ上刷题你会明显感觉到:

  • DP的题目我都能看得懂题解,但要我自己写却没有思路。这时,解题的套路就凸显出了它的重要性。

套路如浩瀚的题海中的一座灯塔,指引我们解题的方向。 ——刘子沛

网上其他的文章会叽里咕噜说一堆东西,常见的有
暴力递归解法——带备忘录的递归解法——递推解法

可你不妨设想一下,这种套路有实质的作用吗?
没有,因为前两步会比第三步还要难写。

那么难道真的没有套路吗?
有的,答案就在下面。


一、DP四步走

1. 状态的表示
2. 状态转移方程
3. 边界
4. 优化

注意:这4步和所谓的 DP三要素 完全不同,后面会讲到。


二、“四步走”的详解

PS:这部分博客会按照四个步骤分为四个小部分,每部分会以一个口诀结束,并会在下方有相应的解释,口诀的作用是方便各位大佬记忆看我多贴心


1. 状态的表示

令 dp[ i ] [ j ] [ k ] = x ,其中 [ i ]、[ j ]、[ k ] 我们将它们定义为状态转移方程的

两个要点:

  1. 每一所表示的状态除了表面状态,还可以表示更深层次、更抽象的东西,DP中较难的题目都是既有维表示表面状态,又有维表示深层次的状态。
    举例:pre , step , id , money 等。
  2. 上面式子中的 x 一般表示题目要求求的值(比如“到达终点最少需要多少步),但是也有例外,如可行性DP(以后会精讲)。

表深结合唯可推

表深结合:上面讲的,不用解释了吧2333
:唯一。意思是说对于你设计的状态,我随便给一个 范围内的状态,比如 dp [ 1 ] [ 2 ] [ 4 ] ,它必须表示唯一的状态
可推:可递推。就是说可以从之前的若干状态自然而然地转移到当前状态


2.状态转移方程

可能有人会问:前面状态都设计好了,状态转移方程不是就呼之欲出了吗?
我回答:可是你以为所有的题目转移方程都像斐波那契数列背包DP等等那么简单吗?

很多题目的状态转移方程还是很复杂的,有可能会用到以下结构:
if / else——分类讨论
max / min——求最值
for / while——遍历大量子状态
&& / || / ^ ——可行性DP

当然,也不局限于这些,只能说具体题目具体分析。

五花八门只看前

五花八门:这个词比较华丽。意思就是说转移时可能会很复杂,“很五花八门”,但我们一定要专注于需要哪些子状态来计算得到当前状态,而不是过分考虑,最终一无所获(后面再考虑优化等问题)。
只看前:不考虑后面怎样怎样,而是只考虑前面的状态,并借助它们进行转移。


3.边界

  1. where?(在哪儿赋边界值)
    考虑求解每个子问题时,有哪些时候“踩空”了(也就是说求解它是前面的子状态不合法),“踩空”的位置及那一类位置就是where

  2. what?(在where的地方赋什么值)
    0:当 x=step / cnt / wealth 时
    1:当 x=方案数 时
    ∞:求min时故意不从这里转移
    -∞:求max时故意不从这里转移
    true/false:可行性DP

踩空处赋各种值

这个……没啥可解释的了吧。


4.优化

这个操作很宽泛,举例:

  1. 矩阵优化,单调队列优化
  2. DP算法本身的优化
  3. 去掉DP状态数组的一些
  4. 各种玄学小技巧类的优化(快读,氧气等等)

自身简化专业玄


坚持看完的都是大佬

建议各位大佬将本文收藏,多读几遍,口诀最好背过,这样效果最好。
本文的初衷是想让大佬们更加大佬化,WE AK NOIP!!!




Wait!!!

信奥中有一句通用法则:

题海战术

套路虽然有用,但是不结合题目来实战,就等于0
我们一起来将这个DP套路应用到实际解题当中:
( DP的核心其实是在思路这块,代码相对而言可以说是呼之欲出的,又限于篇幅,后面就重点讲思路,代码就省略了哈 )


基础应用.

题意:树上多重背包(可百度)
这道题大家在学习DP的时候应该都学过,但你知道解题人是怎么想出这么奇妙的算法的吗?
1.状态的表示
首先,树形DP 99% 的题目都必定有一维表示当前的节点编号
然后,你可能就会懵逼了。接下来呢?那个天才能往别处想呢?
这是,口诀1 就发挥作用了:唯可推。每当我们状态设计到一半时,都要想一想:当前状态是否能表示唯一的操作?在这里,很显然是不能的,为什么?
因为我当前在这个节点,那我对它的子树进行了哪些操作?用了多少背包容量?于是乎,加一个维:占用背包容量大小
可推吗?首先,在直觉上你是可以感觉到可推的,因为我如果买当前节点的话把占用背包容量一减其实就能转移了,具体行不行可以在下一步再考虑。

2.状态转移方程
我们直接从口诀出发,会有一种被导游指引着的感觉,自己也不用有多么强的跳跃性思维,只要一步一步来,就解决问题了Orz
五花八门:实际上是这道题转移太简单了,所以很容易想到要枚举子节点
只看前:这里就需要格外关注了:当我转移时,假设当前节点u的儿子为v,可能只是v并不是最优的,以后还可能变得更优,那为什么现在就着急转移?因为后面的所谓“更优”其实会在v和u各利用一次,导致计算重复

3.边界
老规矩,先想口诀:踩空处赋各种值
那么我们就来找一下踩空处
模板中的做法是对于每个节点,枚举其所有叶子结点来更新值,最后加上当前节点的价值。
容易想到,叶子结点是特例:它没有子节点。但这并不影响做法,因为最后我们总会算上当前节点的价值,而本题又让我们求最大值,所以直接memset为无穷小。错了!!!
应该memset为0。
这是叶子结点就起决定性作用了。叶子节点不枚举其子节点,也就意味着最后的值会是 无穷小+节点价值!
所以,赋值为0的话,可以避免这种情况。
从本题中,你可以学到:

考虑边界时不能忽略特殊情况

4.优化
由于优化是针对线性DP的,对树形DP不太友好,故本题无优化。


可能你会认为,博主直接复制粘贴一个标程,再敲点注释不是又省事又清楚吗?干嘛非要长篇大论讲一堆没用的这种心情很能理解,但是你要明白

不会自己想出来题目的人,做多少题都没用。信奥的学习是注重方法的,而不是注重结果的。看了别人的题解得到启发确实可以马上AC,但是考场上却不能,因为没有了别人的启发,没有过硬的独立思考能力,而本博客正是总结了“思考的套路”。 ——刘子沛大佬


后面我会从洛谷中精选一些绿题和蓝题进行讲解,并且每日更新至少一道题。


  • 本次更新时间为2020.5.24
  • 下次更新时间为2020.5.24

如果你认为本文对你有帮助的话,请素质三连,Thanks♪(・ω・)ノ

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值