洛谷 P3195 [HNOI2008]玩具装箱 —— 斜率优化

This way

题意:

在这里插入图片描述

题解:

洛谷的题解就写的蛮好,首先对于斜率优化,先将它的转移方程写出来,然后对于只包含i的设为A,只包含j的设为B,然后对于含有A和B的项就是二元一次方程中的k和x
这个就可以转成下面的这个式子:
d p [ i ] = m i n ( d p [ j ] + ( s u m [ i ] + i − s u m [ j ] − j − L − 1 ) 2 ) dp[i]=min(dp[j]+(sum[i]+i−sum[j]−j−L−1)^2) dp[i]=min(dp[j]+(sum[i]+isum[j]jL1)2)
然后sum[i]+i就是A
sum[j]+j+L+1就是B
转换式子变成 d p [ i ] = d p [ j ] + A 2 − 2 A B + B 2 dp[i]=dp[j]+A^2-2AB+B^2 dp[i]=dp[j]+A22AB+B2
2AB就是k*x
对于这个函数来说,定量就是k,b变量就是x,y。那么对于当前位置来说,i是定量,已经知道了,j则是未知量
所以函数变成
d p [ j ] − B 2 = 2 A B + d p [ i ] − A 2 dp[j]-B^2=2AB+dp[i]-A^2 dp[j]B2=2AB+dp[i]A2
二元一次方程组的通项是:y=kx+b,那么对于这个方程来说
y : d p [ j ] − B 2 y:dp[j]-B^2 y:dp[j]B2
k : 2 A k:2A k:2A
x : B x:B x:B
b : d p [ i ] − A 2 b:dp[i]-A^2 b:dp[i]A2
然后由于2A是递增的,那么斜率也就是增长的。对于增长的斜率优化来说,最优的点就是队首这个点,如果sta和sta+1的斜率小于k了,那么后面的点一定比前面点更优。对于队尾的话,如果当前点i和en的斜率小于en和en-1的斜率,那么无论k是怎么样的,en都不是最优的,于是将en删除

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
const int N=5e4+5;
ll dp[N],sum[N],L,A[N],B[N];
int st[N];
ll X(int i){return B[i];}
ll Y(int i){return dp[i]+B[i]*B[i];}
int main()
{
    int n;
    scanf("%d%lld",&n,&L);
    for(int i=1;i<=n;i++){
        scanf("%lld",&sum[i]),sum[i]+=sum[i-1];
        A[i]=sum[i]+i;
        B[i]=sum[i]+i+L+1;
    }
    B[0]=L+1;
    int sta=0,en=0;
    for(int i=1;i<=n;i++){
        while(sta<en&&Y(st[sta+1])-Y(st[sta])<2*A[i]*(X(st[sta+1])-X(st[sta])))sta++;
        dp[i]=dp[st[sta]]+(A[i]-B[st[sta]])*(A[i]-B[st[sta]]);
        while(sta<en&&(Y(i)-Y(st[en]))*(X(st[en])-X(st[en-1]))<(Y(st[en])-Y(st[en-1]))*(X(i)-X(st[en])))en--;
        st[++en]=i;
    }
    printf("%lld\n",dp[n]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值