习题:玩具装箱(斜率优化)

题目:
传送门

思路:

首先考虑暴力,那么设dp[i]为以前i号元素的最小值

s[i]为前i号元素的前缀和

那么转移方程即为dp[i]=min{ dp[j]+(i-j-1+s[i]-s[j]-l)^2 }

显然朴素的时间复杂度为O(n^2)

那么我们化简一下式子

设有两个点a,b向i进行转移,且a比b优

那么dp[a]+(i-a-1+s[i]-s[a]-l)^2<dp[b]+(i-b-1+s[i]-s[b]-l)^2

推导过程此处省略

设x[i]=s[i]+1,y[i]=(x[i]+l+1)^2

2*y[i]>=(dp[a]+x[a]-dp[b]-x[b])/(y[a]-y[b])

 

以此为基础,这道题的代码就很容易码出来了

#include<iostream>
using namespace std;
long long n,l;
long long dp[50005];
long long s[50005];
long long head;
long long tail;
long long x[50005];
long long y[50005];
long long q[50005];
double check_=1;
long long pf(long long x)
{
    return x*x;
}
double get_k(long long a,long long b)
{
    return check_*(dp[b]+y[b]-dp[a]-y[a])/(x[b]-x[a]);
}
int main()
{
    cin>>n>>l;
    for(int i=1;i<=n;i++)
    {
        cin>>s[i];
        s[i]+=s[i-1];
        x[i]=s[i]+i;
        y[i]=pf(x[i]+l+1);
    }  
    y[0]=pf(l+1);
    for(int i=1;i<=n;i++)
    {
        while(head<tail)
        {
            if(get_k(q[head],q[head+1])<=2*x[i])
                head++;
            else
                break;
        }
        dp[i]=dp[q[head]]+pf(x[i]-x[q[head]]-l-1);
        while(head<tail)
        {
            if(get_k(q[tail],i)<get_k(q[tail-1],q[tail]))
                tail--;
            else
                break;
        }
        q[++tail]=i;
    }
    cout<<dp[n];
    return 0;
}

 

 

 

 

posted @ 2019-07-30 21:27  loney_s  阅读(112)  评论(0)    收藏  举报