斜率优化总结

适用范围:

斜率优化是DP的一种优化,适用于转移方程式中除了f[i]和f[j]还和其他和ij有关的变量的DP,例如:f[i]=min(f[j]+(a[i]-a[j])^2)(a数组递增)

实现方法:

以上面的方程为例。
step1:展开:
假设f[i]必定由f[j]转移而来,则 f[i]=f[j]+(a[i]-a[j])^2=f[j]+a[i]^2-2*a[i]*a[j]+a[j]^2。
step2:转化:
f[i]+2*a[i]*a[j] = f[j]+a[j]^2 +(a[i]^2)
b + k x = y
(a[i]^2可以省略不看,因为在实际做的过程中会相互抵消)
我们把所有的j都抽象成点对(x,y),由转化的结果可知是(a[j],f[j]+a[j]^2)
那么我们就可以看做是和j有关的一条直线,斜率是2*a[i],而我们想要求的f[i]最小值就是这条直线的截距最小值。
这是我们就可以思考怎样的直线能使这个截距最小。在我们构造出的点图中,因为斜率一定(2*a[i]), 所以一定是在其下凸壳上(如下图)
这里写图片描述
以上就是斜率优化的原理。

特殊情况:

如果只是一般的斜率优化,就要用一些高大上的算法来维护凸壳,但是并不会。。。
但是好在有很多斜率优化的DP题都有一些特殊情况,比如说对于这题来说,x坐标是递增的,即a[i]递增。不仅如此,这题的斜率k也是递增的,即2*a[i]递增。那么我们只要写一个单调队列来维护一下就好了。

附:维护单调队列方法:
每一次将队列首head和head+1求一下斜率,记作xl(que[head],que[head+1]),求的方法也就是数学中的两点连线的斜率求法,即为(ya-yb)/(xa-xb)。
将其与当前i的斜率比较,如果小于它,那么队首对于后面的元素的修正就没有用了(显然可以找到更优的,即y坐标更小的),就将其踢出队列。
然后用队首来修正f[i](因为是单调队列,所以此时队首的y坐标肯定最小)。
接下来再将xl(que[tail-1],que[tail])与xl(que[tail],i)比较,如果前者大于后者就可以将其踢出队列,理由和前面一样。最后将i加到对尾就行了。

示例代码:

    for (int i=1;i<=n;i++) {
        while (head<tail&&xl(que[head],que[head+1])<x[i]) head++;
        f[i]=f[j]... (修正)
        while (head<tail&&xl(que[tail-1],que[tail])>xl(que[tail],i)) tail--;
        que[++tail]=i;
    }

OVER。。。

猜你喜欢

转载自blog.csdn.net/dyt_b/article/details/79426707