DP专题
区间动态规划
1、回文字符串
板子。因为整个区间只能变成一个回文串,所以只需要考虑第r个和第l个是否相同。
这样\(O(n^2)\)的,或许也有三方算法。。。
这其实也告诉我们区间动态规划多有多种转移方法,选择最大程度利用小区间时间复杂度最小的算法。
通常会有分割成两个小区间/考虑最左右两个等
2、字符串折叠(这都什么时候的题了)
N太小了。暴力考虑一个区间能否整个被折叠即可。
即合并两个区间或者区间内部折叠两种转移方式。
动态规划的另类优化
真的很“另类”
1、争渡
显然的前缀和,没啥可说的。
2、小朋友吃糖果
最暴力的想法,三维:i类,第一个人是j,第二个人是k的方案数
明显可以差值优化,这是一种非常非常常用的方法,在答案只需要求\(j=k或j-k=\lambda\)时使用
可以优化到四次方
还是过不了
我们来考虑这个神必柿子
确实是一种非常好的方法。在转移的时候考虑一下有\(\sum\)的柿子能不能通过前一个dp递推地求出。
以后一定注意这种题目。
注意推导的时候有一些坑不能踩:
除法是向下取整的除法,不能直接两个加到一起!!!
绝对值很容易漏掉。
3、木棍分割
肯定先二份答案
然后发现转移的时候会使用到符合条件的一段区间的和。
可以通过区间数据结构来优化这种转移。
但是我们发现符合条件的区间是单调不减的。
于是我们可以用类似单调队列的东西去维护这一段即可。
4、整数划分
减少状态数量
一个数的因数个数小于\(\sqrt N\)!!!!!!!!!!!!!!!!!!!
一定记住这个结论,受用终身。
5、选数字
和4差不多。可以用map来存。一个log而已。
6、回文串划分
看到之后马上打了个manacher,然后发现P用没有????
直接暴力解决\([i,j]\)是否是一个回文串。然后直接dp即可。
7、字符王国
神必题。有环就无解。所以没有环。所以暴力记录每一种字母出现次数。所以就可以拓扑排序做了。
8、最长递增路径
神神神神神神神必题!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
这种题目已经见多了。
因为点可以多次经过,边只能走一次,我们不难想到以边作为dp维度。
发现权为w的边只会从<w的其它边更新来。
所以按照边权排序,逐条加入图中即可。
老套路了,要记住。
注意处理边两头互相更新和边权相同的情况。
子段和问题
1、循环数组最大子段和
\(yjt\)CCCCCCCCCCCCCCCCCCOrz
首先\(n^2\)暴力拆环
其次可以考虑子段和就是\(S_i-S_j\),对于一个i用线段树暴力维护
当然也就可以用单调队列\(O(n)\)维护了。
还有一种方法。
考虑做链时这个环只有前面一段后面一段的统计不到。可以转化为前面一段后面一段求最大值的问题。
可以发现可以转化为找到中间的某一段子段和最小,其它的就是答案。
2、最大子矩阵和
暴力做。
考虑枚举i行和j行。
求i行和j行内的子矩阵最大值。
枚举一个k行表示当前矩阵在k到j行,用最大子段和计算即可。
相当于压缩几行成为一行。
3、最大子段和 V2
想了很多错误的方法。实际上最后还是挺简单的。。。
考虑到交换分块答案内部和答案外部进行交换。或者内部交换到内部等其他情况,这些就不会对答案造成影响。分类讨论一下即可。
所以主要解决第一种情况。
我们考虑i和k交换,且k在i左边的情况(另外一种情况倒着做一遍即可),然后设\(f[i]\)为交换后以i结尾的最大值。
即
转移\(f[i+1]\)的时候分情况。首先可以继承原来的最大值加上最后一项\(A_i\),或者不继承就是所有\(A_k\)最大值。
4、最大M子段和 V2
做过的题目。先取所有正数段
之后要么取一个负数段连接两个,要么某个段不取。
这样都可以使得取的段数-1
可以用反证法证明这种方法是最优的。
计数类动态规划
1、子序列个数
我们需要考虑的是当前的前i个数中,最后一个数的出现对答案的影响是什么。设\(f[i]\)为前i个数的答案。
如果最后一个数不选,那么答案就是\(f[i-1]\),由于定义,这里面是没有重复区间的。
但是如果最后一个数选了,在另一个\(f[i-1]\)的基础上就会出现很多重复的区间。可以发现如果上一个\(a[i]\)在\(j\)位置,那么应该会有\(f[j-1]\)个重复的区间,减去即可。
2、最长递增子序列的数量
好题。
先考虑一般的LIS的一种求法:
某个数字只会被比它小而且在它前面的数字更新到。
本质上就是一个二维偏序问题了。
所以可以按照数字大小排序后依次dp。树状数组下标对应数字的下标。
注意处理一下值相同的情况即可。
然后再想想树状数组能够统计什么东西。
最简单的是区间求和单点修改。
当然利用差分可以单点求和区间修改。
但是其实也可以单点修改和求前缀和最值。
那么这道题怎么做呢?
其实也是差不多的,同样的方法,利用树状数组统计当前区间最大值和当前区间最大值的个数即可。
重点在转化为二维偏序问题。
这启发我们以后在遇到类似转移的时候由某些特定的有限制的状态转移来的时候,能不能通过某些方法(特别是排序)只枚举到这些状态点,再通过数据结构等方法去优化它。
这个题的思想和上面那个图上dp是想通的。
3、有限制的排列
所以我们需要知道一个普遍的排列dp的思路!
dp某个排列的方案时,可以考虑从前\(i-1\)个数的排列转移到前\(i\)个数的排列,通过枚举排列的最后一个数是什么。
如果最后一个数是\(j\),那么就相当于前\(i-1\)个数中\(≥j\)的所有数加1
(但是这并不影响原排列的相对大小关系)
然后把第\(j\)个数加入最后
然后我们只需要考虑最后一个位置带来的影响就行了。
所以这个题,我们可以先把所有限制转化为\(i\)和\(i-1\)之间的大小限制。
那么我们在枚举出一个\(j\)用\(f[i-1][k]\)转移到\(f[i][j]\)的时候,如果有某些限制,那么我们对应的枚举的k进行变化即可。
最后k的枚举可以通过前缀和给压掉
4、逆序排列
一下想不到正解,就从暴力dp开始。
T个询问,只需要处理所有的ij即可。
沿用上面那个题的思路,设\(f(i,j,k)\)表示前i个数的排列,最后一个数是k,有j个逆序对的方案数。
那么有
\(j-(i-k)\)是因为第i位是k那么说明前面有\(i-k\)个数比它大,会带来\(i-k\)个逆序对。
我们发现这个\(l\)可以用前缀和给压掉
其中
然后我们又发现最后的答案好像和\(k\)也没有什么关系,所以k这一维完全可以压掉了。
再用前缀和优化,时间复杂度就能过了。
小Y的疑惑
神仙题。多来吸收一下思想!!
在普通背包问题上面加入了一些神奇的限制。那么这个限制是我们唯一的突破平方的口。
可以发现在重量大的时候有很多个物品没有任何用处。具体地,在\(i>\sqrt N\)的时候就不可能用完所有的物品了。
\(\sqrt N\)我们是非常熟悉的。分块、莫队、BSGS等算法里面都会出现这样的复杂度。因为分为\(\sqrt N\)块是最佳复杂度。
这个题也一样!这是否表明我们可以把转移分为两块,每一块都是\(N\sqrt N\)?
当然可以!
我们可以先考虑前\(\sqrt N\)个物品,是一个分组背包。但是!每一组物品的重量相同且相邻的相差1。
由于我们知道下一个转移的时候会用到\(j-k,j-2k,j-3k,...\)的f值,前缀和预处理即可实现\(N\sqrt N\)的复杂度了。
在考虑后面一段。NOIP2001数列分段其实就是它的弱化版!!!!!!
一个非常重要的性质:如果我们把选择的所有物品一定是一个单调不下降的序列且全部都要大于\(\sqrt N\)
新加入一个大小为 \(\sqrt n+1\) 的数。
将之前 \(i\) 个数每个都加 \(1\)
显然这样是对的,因为最优的状态一定会被我们给枚举到的。且最多只有 \(\sqrt n\)个数。
所以这个想法的妙处在于限制的组数或是物品数都是\(\sqrt N\)级别,所以我们可以大胆枚举背包的容量。
最后合并的柿子是一个卷积形式。就是乘法原理。
本文来自博客园,作者:lei_yu,转载请注明原文链接:https://www.cnblogs.com/lytql/p/15256770.html