本文为二十一世纪二十年代的最新产物,作者:刘子沛
微信号:zipeiliu
想转载请随时联系我
本文内容可能过于丰富,建议先扫一遍,然后再分成几个部分每天读一部分,因为这样你才能掌握本文的精髓
动态规划 (DP) 是一个很万能的东西,好比数学中的方程。它也是信奥学习中的重点和难点,因此把它学好至关重要。如果你去洛谷等OJ上刷题你会明显感觉到:
套路如浩瀚的题海中的一座灯塔,指引我们解题的方向。 ——刘子沛
网上其他的文章会叽里咕噜说一堆东西,常见的有
暴力递归解法——带备忘录的递归解法——递推解法
可你不妨设想一下,这种套路有实质的作用吗?
没有,因为前两步会比第三步还要难写。
那么难道真的没有套路吗?
有的,答案就在下面。
1. 状态的表示
2. 状态转移方程
3. 边界
4. 优化
注意:这4步和所谓的 DP三要素 完全不同,后面会讲到。
PS:这部分博客会按照四个步骤分为四个小部分,每部分会以一个口诀结束,并会在下方有相应的解释,口诀的作用是方便各位大佬记忆 ,看我多贴心 。
令 dp[ i ] [ j ] [ k ] = x ,其中 [ i ]、[ j ]、[ k ] 我们将它们定义为状态转移方程的维。
两个要点:
表深结合唯可推
表深结合:上面讲的,不用解释了吧2333
唯:唯一。意思是说对于你设计的状态,我随便给一个 范围内的状态,比如 dp [ 1 ] [ 2 ] [ 4 ] ,它必须表示唯一的状态
可推:可递推。就是说可以从之前的若干状态自然而然地转移到当前状态
可能有人会问:前面状态都设计好了,状态转移方程不是就呼之欲出了吗?
我回答:可是你以为所有的题目转移方程都像斐波那契数列和背包DP等等那么简单吗?
很多题目的状态转移方程还是很复杂的,有可能会用到以下结构:
if / else——分类讨论
max / min——求最值
for / while——遍历大量子状态
&& / || / ^ ——可行性DP
当然,也不局限于这些,只能说具体题目具体分析。
五花八门只看前
五花八门:这个词比较华丽。意思就是说转移时可能会很复杂,“很五花八门”,但我们一定要专注于需要哪些子状态来计算得到当前状态,而不是过分考虑,最终一无所获(后面再考虑优化等问题)。
只看前:不考虑后面怎样怎样,而是只考虑前面的状态,并借助它们进行转移。
where?(在哪儿赋边界值)
考虑求解每个子问题时,有哪些时候“踩空”了(也就是说求解它是前面的子状态不合法),“踩空”的位置及那一类位置就是where
what?(在where的地方赋什么值)
0:当 x=step / cnt / wealth 时
1:当 x=方案数 时
∞:求min时故意不从这里转移
-∞:求max时故意不从这里转移
true/false:可行性DP
踩空处赋各种值
这个……没啥可解释的了吧。
这个操作很宽泛,举例:
自身简化专业玄
坚持看完的都是大佬
建议各位大佬将本文收藏,多读几遍,口诀最好背过,这样效果最好。
本文的初衷是想让大佬们更加大佬化,WE AK NOIP!!!
信奥中有一句通用法则:
题海战术
套路虽然有用,但是不结合题目来实战,就等于0
我们一起来将这个DP套路应用到实际解题当中:
( DP的核心其实是在思路这块,代码相对而言可以说是呼之欲出的,又限于篇幅,后面就重点讲思路,代码就省略了哈 )
题意:树上多重背包(可百度)
这道题大家在学习DP的时候应该都学过,但你知道解题人是怎么想出这么奇妙的算法的吗?
1.状态的表示
首先,树形DP 99% 的题目都必定有一维表示当前的节点编号。
然后,你可能就会懵逼了。接下来呢?那个天才能往别处想呢?
这是,口诀1 就发挥作用了:唯可推。每当我们状态设计到一半时,都要想一想:当前状态是否能表示唯一的操作?在这里,很显然是不能的,为什么?
因为我当前在这个节点,那我对它的子树进行了哪些操作?用了多少背包容量?于是乎,加一个维:占用背包容量大小。
那可推吗?首先,在直觉上你是可以感觉到可推的,因为我如果买当前节点的话把占用背包容量一减其实就能转移了,具体行不行可以在下一步再考虑。
2.状态转移方程
我们直接从口诀出发,会有一种被导游指引着的感觉,自己也不用有多么强的跳跃性思维,只要一步一步来,就解决问题了Orz
五花八门:实际上是这道题转移太简单了,所以很容易想到要枚举子节点
只看前:这里就需要格外关注了:当我转移时,假设当前节点u的儿子为v,可能只是v并不是最优的,以后还可能变得更优,那为什么现在就着急转移?因为后面的所谓“更优”其实会在v和u各利用一次,导致计算重复
3.边界
老规矩,先想口诀:踩空处赋各种值
那么我们就来找一下踩空处
模板中的做法是对于每个节点,枚举其所有叶子结点来更新值,最后加上当前节点的价值。
容易想到,叶子结点是特例:它没有子节点。但这并不影响做法,因为最后我们总会算上当前节点的价值,而本题又让我们求最大值,所以直接memset为无穷小。错了!!!
应该memset为0。
这是叶子结点就起决定性作用了。叶子节点不枚举其子节点,也就意味着最后的值会是 无穷小+节点价值!
所以,赋值为0的话,可以避免这种情况。
从本题中,你可以学到:
考虑边界时不能忽略特殊情况
4.优化
由于优化是针对线性DP的,对树形DP不太友好,故本题无优化。
可能你会认为,博主直接复制粘贴一个标程,再敲点注释不是又省事又清楚吗?干嘛非要长篇大论讲一堆没用的这种心情很能理解,但是你要明白
不会自己想出来题目的人,做多少题都没用。信奥的学习是注重方法的,而不是注重结果的。看了别人的题解得到启发确实可以马上AC,但是考场上却不能,因为没有了别人的启发,没有过硬的独立思考能力,而本博客正是总结了“思考的套路”。 ——刘子沛大佬
后面我会从洛谷中精选一些绿题和蓝题进行讲解,并且每日更新至少一道题。
如果你认为本文对你有帮助的话,请素质三连,Thanks♪(・ω・)ノ