首页 > 代码库 > 【以前的空间】斜率优化的一点点总结
【以前的空间】斜率优化的一点点总结
今天有看了一道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。
然后就是这样了!
【以前的空间】斜率优化的一点点总结