(C++)DP动态规划

天下苦DP久已。

​ DP非常重要,2022年蓝桥杯应该改名为DP杯,至于2023年那个我觉得应该叫做暴力杯。

​ DP的核心思想在于,通过前面几个状态来推导下一个数据是什么,也就是走一步是一步。其本质实际上是记忆化搜索,但是有些玄学的事情就是,有时候记忆化会因为玄学递归问题TLE,但DP的那四五层for循环就迷之过了。


​ DP题的核心在于两个事情:

一个就是你要知道这题可以DP

​ 是否有后效性,对于某个问题,如果我们只关注某个状态,只关注状态的值,而不关注该状态是如何转移过来的,那么就是一个无后效性的问题,此时可以使用DP解决。

​ 用人话来说就是,只会用上一步计算出下一步,前面其他步骤都不对现在的步骤产生任何影响。

 大问题的最优解可以由小问题的最优解推出,这个性质叫做“最优子结构性质”。

就是说你能从一个小问题一路推到问题本身,就像斐波那契数列一样,你得从1+1开始推,然后一路推到你当前需要的数字里头。

然后就是你要知道怎么得到状态转移方程

​ 这步才是真正麻烦的事情。状态转移方程的目的就是利用前一状态,通过特定但是通用的式子,来得到当前状态的最优答案。但到底用什么做状态,有个很容易理解的事情。很多人都说01背包问题才是DP的经典入门题。我觉得不是。

​ 有请真正经典例题:NOIP2004普及组 花生采摘

​ 题目不再在这里复述了,自个去里头看。但是为什么我说这题才是经典入门题呢?这题能很好并且很简单的体现DP该有的一切。他的状态转移方程原理非常清晰明了:\(f[y][x] = max(上方点最大采摘数, 左方点最大采摘数) + 当前点花生数\)。在方程里,我们可以立刻确认一件事情:状态是位置,方案最优解来自于当前位置的花生数和上一步可以得到的最大花生数。

​ 那么到底什么可以作为状态?你可以把状态转移方程比喻成一个特殊的函数,这个函数需要你输入特定的自变量才能得到结果,而那些自变量就如同构成了坐标轴一样(就好像花生采摘的平面坐标一样)构建了一个坐标系,我们需要用当前的坐标,配合题目条件去计算下一个坐标在哪。用这个思维去看01背包问题 - NOIP2005普及组 采药,到底是什么可以构成我们的坐标系呢?价值要作为答案的输出,所以肯定不是,那么就剩下第几种药和已用的时间,换成裸的01背包描述就是,我们用当前物品编号剩下多少时间组成了自变量,得到了二维的方程。

​ 选用当前物品编号(而不是选用已经选择了多少个物品)作为其中一个状态的原因是,我们需要确认我们选了什么,这样才能从其他状态得到对应的值。如果用的是取了多少个物品,我们并不知道到底取用了哪些物品,那就找不到状态在数组中对应的位置,就没法把状态转移方程推导下去。

​ 那么到了多重背包里呢就是多枚举一下每个物品塞多少个,塞不塞得下,因为多重背包本质就是这个物品重复几次出现而已。至于完全背包问题,则是通过倒着推背包空间来获得解法。

​ 通过以上总结可以发现,可以作为状态的参数必须是可用于计算上/下一个状态是什么,这样才能找到下一个状态位置,搞出状态转移,进而使用状态转移方程计算结果。

posted @ 2024-03-16 21:20  ComputerEngine  阅读(61)  评论(0编辑  收藏  举报