首页 > 代码库 > 【以前的空间】斜率优化的一点点总结

【以前的空间】斜率优化的一点点总结

  今天有看了一道dp题,发现好像裸不能过,应该是要斜率优化,结果发现自己那点傻×智商早把这东西忘得差不多,而且当时也是有点乱不是弄得很懂。于是又花了一个早上来整理下。

 

  《用单调性优化动态规划》  

   这个东西的话很好。但是由于我蒟蒻所以看不懂。先找了模板题到网上找题解,然后跟着题解自己推。那么我就用《[ZJOI2007]仓库建设》来推吧!

 

  首先很容易写出状态转移方程

  f[i]:=Min(f[j]+w[j,i])+c[i]

  其中f[i]表示前i个的最优状态且在i建一个仓库,j表示上一个仓库的位置。

  w[j,i]=p[j+1]*(x[i]-x[j+1])+p[j+2]*(x[i]-x[j+2])+……+p[i-1]*(x[i]-x[i-1])+p[i]*(x[i]-x[i])(p.s 按照论文里面的说法:为了之后的式子好看保留最后一项)。

  于是朴素的话就是枚举i之前的j,这样复杂度是o(n2)。

  我们把w[j,i]这个式子展开,再合并,得到

  w[j,i]=x[i]*(p[j+1]+p[j+2]+……+p[i])-(p[j+1]*x[j+1]+p[j+2]*x[j+2]+……+p[i]*x[i]);

  记sum1[i]=Σp[i],sum2[i]=Σp[i]*x[i],然后w[j,i]可化为

  w[j,i]=x[i]*(sum1[i]-sum1[j])-(sum2[i]-sum2[j])

        =-x[i]*sum1[j]+sum2[j]+x[i]*sum1[i]-sum2[i];

  然后把w[j,i]带入原来的方程得到

  f[i]:=Min(f[j]+sum2[j]-x[i]*sum1[j])+x[i]*sum1[i]-sum2[i]+c[i] (后面几个都是根据i唯一确定的,所以可以从Min中提取出来)

  然后就是斜率优化的东西了!

  把Min中由i确定的看成是系数,也就是这个方程中的-x[i],先设为a。

  把与系数相乘的那个设为x,也就是这个方程中的sum1[j]。

  把剩下的那些东西都设为y,也就是这个方程中的sum2[j]+f[j]。

  最后把f[i]设为G。

  然后就变成G=ax+y,进而得到y=-ax+G。之后就是论文里面一些balabala的东西了。

  但是我觉得这样解释不如另一种解释斜率优化好(便于像我这样的蒟蒻理解):

  

  设j,k为i之前两个决策点,且i>j>k,以及j决策要优于k决策

  那么f[j]+w[j,i]<f[k]+w[k,i]

  也就是f[j]+sum2[j]-x[i]*sum1[j]<f[k]+sum2[k]-x[i]*sum1[k]

  移项就是f[j]+sum2[j]-f[k]-sum2[k]<x[i]*(sum1[j]-sum1[k])

  然后用之前说的x,y,a代替就是

  y(j)-y(k)<a*(x(j)-x(k))

  然后x是单调递增的所以x(j)>x(k),所以除过去得到

  (y(j)-y(k))/(x(j)-x(k))<a 然后这个式子不就是斜率的公式么……

  所以若k<j且j优于k则满足上面这个式子。

 

  然后就是维护规则了。

  维护一个单调队列,里面记录决策的位置,且里面的决策的位置都是单调递增的。

  首先对于当前i来说,它的最优决策就是满足(y(j)-y(k))/(x(j)-x(k))<a的最大一个j。于是从队头head开始,如果(y(head+1)-y(head))/(x(head+1)-x(head))<a就是head++,这样就可以找到(y(j)-y(k))/(x(j)-x(k))<a的最大一个j。也就是这时的head就是i的最优决策,于是f[i]=f[q[head]]+w[head,i]+c[i]。

  然后就是把i加入队列中,同样也是要满足(y(j)-y(k))/(x(j)-x(k))<a,不过是要考虑进三个决策之间斜率的关系。由于斜率是单调递增的,且(y(tail)-y(tail-1))/(x(tail)-x(tail-1))<a(tail),(y(i)-y(tail))/(x(i)-x(tail))<a(i),所以(y(tail)-y(tail-1))/(x(tail)-x(tail-1))<(y(i)-y(tail))/(x(i)-x(tail)),按照这个规则维护,如果tail不符合这个规则那么tail--,直到tail符合,然后把决策i加入决策队列中,即q[++tail]=i。

  然后就是这样了!

【以前的空间】斜率优化的一点点总结