之前我们学习过动态规划方法,但是并没有对DP进行系统细致的优化。今天来看一下DP的优化方法。

  • 一、矩阵优化
    线性代数教材中可能讲过,通过矩阵进行快速的重复运算(矩阵快速幂)。
    (以下是矩阵快速幂的写法)
  • 强化学习动态规划 动态规划优化方法_强化学习动态规划

  • 直接看题:
    求斐波那契数列第n项余1e9+7后的值,其中1<=n<263
    通过使用普通的动态规划(递推)显然会爆掉,矩阵加速就十分使用与这种线性的递推DP,构造矩阵再配上矩阵的快速幂,以logn的时间复杂度求出答案。
  • 强化学习动态规划 动态规划优化方法_动态规划_02

  • 02 单调队列优化
    多重背包的时候就可以用到,主要突出的是单调性。
    讲解视频 例题:n个数的序列,求每一项前m个数的最小值,不足m项从第一项开始,1<=m<n<=1e5
    可以维护一个双端队列,队列中记录序列位置,队列中的数字在序列中对应位置的值大小单调递增。每次处理完一个数将其与队列尾部比较,如果比队列尾部小或相等则弹出尾部,一直到队列为空或队列尾部比当前这个值小,将这个位置插入队列尾部。计算当前位置的答案时先将位置太小的首部弹出,然后取队列首部的位置即可。
    时间复杂度O(n)。
  • 03 线段树优化
    具体可以见我之前写过的线段树这篇文章
  • 04 斜率优化
    当状态转移方程:
  • 强化学习动态规划 动态规划优化方法_强化学习动态规划_03

  • 𝜔(i, j)包含与i有关的项和与j有关的项的乘积如下,且已知a[i]和b[i]单调递增,该如何处理?

做变换:

强化学习动态规划 动态规划优化方法_队列_04


等式就可以看作一条直线,每个j都确定出一个点(x, y),对于当前要求解的i,其斜率已确定(且随i单增),要使dp[i]最大即要使这条直线过某一个j形成的点并使得的截距最小。

强化学习动态规划 动态规划优化方法_队列_05


强化学习动态规划 动态规划优化方法_强化学习动态规划_06


强化学习动态规划 动态规划优化方法_强化学习动态规划_07


假设之前已处理完前9个点,将他们画在坐标平面上,可以发现,不论当前点a[i](斜率)是多少,能使截距最小的点一定选在标出来的四个点形成的下凸包中。且当𝑘_𝑗1𝑗2≤𝑘≤𝑘_𝑗2𝑗3时,选择j2点最优。

可以用双端队列来维护这个下凸包,当前双端队列中按顺序存放有j1~j4四个点。

判断当前计算的i的斜率是否大于𝑘_𝑗1𝑗2(队列中前两个点的斜率),若大于,则把队首弹出,因为i的斜率会越来越大,队首之后肯定也不会是最优转移点,弹出后继续判断。然后取队首作为转移点,此时直线的截距一定最小。

设刚计算完的点为j5。

判断𝑘_𝑗3𝑗4(即队列末尾两个点形成的斜率)是否大于𝑘_𝑗3𝑗5(j5与队列末尾倒数第二个点的斜率),若大于则将队尾弹出,因为加入j5后原队尾已经不在下凸包上,之后肯定不会作为最优转移点,弹出后继续判断。

将j5插入到队列的尾部。

强化学习动态规划 动态规划优化方法_算法_08


建议使用手写的双端队列,因为会访问到首部第二个元素和尾部第二个元素,用stl里的deque会麻烦一点