斜率优化总结

斜率优化

斜率优化是用于优化一些线性DP,主要思想类似于凸包。
下面来看一个例题:HDU 3507
由题意不难想到是DP,也很容易退出转移过程 f[i]=min(f[j]+(s[i]s[j])2)+M(i[0,i1])
但是一看 n 的范围马上就萎了,下面来看看用斜率怎么优化。

现将推出的转移方程化简:f[i]=min(f[j]+s[i]2+s[j]22s[i]s[j])+M
显然 s[i]2 对于当前状态 i 来说是个定值,所以移出来f[i]=min(f[j]+s[j]22s[i]s[j])+M+s[i]2
下面来看如何将它看成一条直线
x=s[j],y=f[j]+s[j]2,k=2s[i](ki)
那么 b=kx+y
y=kx+b
min(b) 就是我们要的最小值。

这里写图片描述

既然已经转化为直线,那么就我们就是要拿一条斜率固定的直线在一个有许多点的平面内寻找交点,
如果要使截距 b 最小,那么肯定是这条直线肯定与平面的点所形成的的下凸壳相交,
所以在寻找答案的同时我们还要维护一个下凸壳。

再回到例题中,因为题目中的斜率为2s[i],是单调递增的,
对比图会发现,随斜率的增长,直线与下凸壳的交点是不断向 x 轴的正方向移动
这用一个指针标记就可以了。

效率显然是O(n)的。

拓展

若跳出这道题去看斜率优化,会出现许多情况。
如果从转移方程中推出k不满足递增,那么就要二分在下凸壳上寻找。
如果连x的坐标都不能满足递增,就需要用数据结构维护下凸壳。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=500005;
LL s[maxn],f[maxn];
struct jz{
    LL x,y;
    jz(LL a=0,LL b=0): x(a),y(b) {};
    jz operator-(jz &b){return jz(x-b.x,y-b.y);}
}a[maxn];
int n,m,til,now;
inline int _read(){
    int num=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') num=num*10+ch-48,ch=getchar();
    return num;
}
LL getb(jz x,LL k){return -k*x.x+x.y;}
LL check(jz x,jz y){return x.x*y.y-x.y*y.x;}
int main(){
    freopen("exam.in","r",stdin);
    freopen("exam.out","w",stdout);
    while (scanf("%d%d",&n,&m)==2){
        for (int i=1;i<=n;i++) s[i]=s[i-1]+_read();
        memset(f,63,sizeof(f));
        f[0]=0;til=now=1;a[1]=jz(0,0);
        for (int i=1;i<=n;i++){
            LL k=2*s[i];
            while (now<til&&getb(a[now+1],k)<getb(a[now],k)) now++;
            f[i]=getb(a[now],k)+m+s[i]*s[i];
            jz x(s[i],f[i]+s[i]*s[i]);
            while (til>1&&check(a[til]-a[til-1],x-a[til-1])<=0) til--;
            now=min(til,now);a[++til]=x;
        }
        printf("%lld\n",f[n]);
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值