学习dp也有一段时间了,从刚开始连理解背包都难以做到,到现在已经能独立地写一些不算复杂的dp题目,对于dp有了一点自己所谓的理解,在此处写下纯粹是对于自己想法的梳理,可能不够成熟,甚至有很多纰漏,请谨慎理解。
何为DP?
dynamic programming,中文译为动态规划,本质是为了解决策略过程最优化的一种数学方法,这边的过程是个人觉得最重要的部分:
在初次接触dp的时候(背包问题),个人最大的疑惑便是看不懂它为啥能过通过处理[0 <= x <= V]范围内所有的背包解决问题,他这些中间大小的背包到底起到了啥作用。
之所以我刚开始理解不了,很大的原因是我本人缺乏思考,在很多题目之上,我只会去想起点:问题所给出的条件和终点:我要求的答案,对于中间的过程量完全不重视,但dp过程中过程量是十分重要的,和简单的递推一样,你只有先得到中间量,才能继续往后推。
解决dp问题的方式和解决分治,递推之类题目的方式十分相似,就是把一个问题转化几个子问题,然后解决了子问题就能推倒出这个问题的答案,很多奆佬们就是这么解释dp的,刚开始是个纯萌新的我对于理解这种说法感到压力山大,现在的我能理解,但不太喜欢这么表述,下面我将尝试用自己的理解来讲讲dp。
DP最重要的是什么?
个人认为状态转移方程是dp问题中最重要的东西,当你推导出正确的状态转移方程,你就可以很简单地写出能够ac的代码,而对于状态的理解则是推倒出状态转移方程的关键。
那么状态是啥?打个比方:背包dp里面的状态——考虑i个物品时体积为j的背包的最优情况,考虑了n个物品和小于等于m的所有体积的背包;一般区间dp里面的状态——范围为[i,j]的区间的最优情况,考虑了表示区间范围最常用的元素,左边界和右边界。
简单来说,状态可以看做一个包含所有元素的结点,就像你写一个结构体,给里面添加上所有必要的要素,该状态下的最优解,我们一般都用数组表示,几个必备要素就开几维,还方便访问。且状态必须要有以下几点。
1.状态是有起点的
有起点你才能推过程,就像背包问题0体积的背包,区间dp的单点权值等等。
2.状态是可以转移的
根据题目给出的条件,你可以找到转移的条件,背包问题的物品,区间问题改变区间的方式等等。
3.状态无后效性
即已经确认的最优状态,在后续的状态转移中不会再回到这个状态,并对答案造成影响。
4.状态是有限的
在时间复杂度和空间复杂度的范围内有限,可以全部表述清楚,(空间上常用滚动数组之类的操作优化,时间上背包有单调队列优化,部分区间dp有四边形不等式优化等等,懂的少,不多逼逼)。
最后总结
dp是一个需要时间思考和不断刷题才能提升的专题(可能只有蒟蒻我是这样的,感觉平常刷题学到的一些东西很有用,有时候看到新题就感觉老题目的模型改改就好了),以及他还需要一点灵性,将问题的模型转变为简单的dp问题。
最开始接触这个词,是从dp求最大矩阵的过程中学到的,把矩阵进行一个nnm的预处理后,就可以简单的贪心求结果了。(此处的降维不是指滚动数组的降维,是个人理解的把题目模型化简,改变本质的一种降维。)
1.LIS的 O(nlogn)复杂度的算法,本质上是从暴力dp降维成了简单的二分贪心
例题:P1020 导弹拦截(经典例题,无需多言)
例题:P1233 木棍加工(二维转一维,然后就变成了LIS模板题)
2.简化问题,分解模型(范围太广,不知道怎么归类,比较看灵性的兄弟)
例题:P1388 算式(神仙题目,单纯插入*法的做法是个假dp,看完题解发现先枚举加号和乘号位置就转化成了一个简单的区间dp,但测试样例好像有bug)
字符串的题目很多,简单的基础dp的字符串转变,匹配;简单的字符串dp。难的有字符串匹配加dp(用到ac自动机啥的)。
1.删减添加的字符在一般dp中可以看做从[i-1][j]或者[i][j-1]中传递状态而来(如果限制首尾同样适用于区间dp)。
例题:P1140 相似基因(看做简单的添加字符)
例题:P1435 回文字串(隐藏条件加头尾)
2.修改一个字符可以看做从[i-1][j-1](仅限于字符变化题目)
例题:P2758 编辑距离(裸题)
1.按照题意,贪心输出即可。
例题:P1281 书的复制(找出最小值之后从尾到头贪心输出即可,就遇到一次)
2.设置bool数组或者int数组进行回溯,一种直接记录物品,一种对物品设置true的标记。
例题:P1759 通天之潜水(二维背包路径回溯)
例题:P2066 机器分配(直接记录物品数量)
dp出必要值之后,从全局换根即可,很骚的操作,但很好用。
例题:P1364 医院设置(这题数据太小,但动态转移方程好写,可以试手)
拿右下角符合条件的1判一次[i-1][j-1]、[i-1][j]、[i][j-1]即可,只要把题目模型转换出来即可(难点)。
例题:P1387 最大正方形(裸题)
例题:[USACO5.3] 巨大的牛棚Big Barn(裸题)
例题:P1681 最大正方形II(简单变形)
在dp第一个东西的同时,在第一个dp的条件下dp第二个东西。
例题:P1509 找啊找啊找GF(01背包)
1.除了简单的权值前缀和,我们可以对两个元素取值1与-1,用前缀和实现绝对值的计算,也可以把较少的元素hash完后变成异或前缀和,利用异或前缀和的性质,完成一些题目。
例题:P1564 膜拜(1与-1,算绝对值)
例题:P2697 宝石串(1与-1,区间和为0)
2.二维前缀和对于一些固定大小的矩阵题十分有用,而且便于入门。
例题:P2004 领地选择(裸题)
1.线性差分的经典运用便是区间涂色问题。
例题:P2434 [SDOI2005]区间
2.二维差分是一种操作很简便的解决矩阵区间变化的题目。
例题:P5542 [USACO19FEB] Painting The Barn S(裸题)
目前遇到的都是当天可变,所以直接日结即可,分解为多个完全背包。
例题:P1853 投资的最大效益
例题:P5662 纪念品
一些树上问题,利用拓扑排序的无后效性来做非常合适。
例题:P2196 挖地雷(这题其实可以不用拓扑排序,因为按矩阵处理已经无后效性了,但也可以用拓扑写)
例题:P3183 [HAOI2016]食物链(细节,注意成链即可)
一些题目可以通过取模实现状态有限化。
例题:P2946 [USACO09MAR]Cow Frisbee Team S(裸题)
例题:P2049 魔术棋子(裸题)
还有之前很多没在洛谷刷的题目没放上来,这里值算了真普及组我觉得做的一些小收获,之后再整理之前的一些dp题目,路漫漫其修远兮。