斜率优化

如果转移方程中含有 既有i 的项又有j的项  往往可以考虑斜率优化

斜率优化的目标是将dp式转化为 y=kx+b  这种形式  

P3195 [HNOI2008]玩具装箱TOY

题意:有1-n个玩具需要打包  每个玩具的有其长度  可以一段区间一段区间地打包 问打包完所有地玩具需要的最小代价

打包一段区间的代价为  玩具个数-1 加上所有玩具的长度 减L 的平方   (L为给定值)

dp的转移式很好得到:$dp[i]=min(dp[j]+(sum[i]+i−sum[j]−j−L−1) ^2 )  (j<i)$

设 $a[k] = sum[k] + k$

     $ b[k] = sum[k] + k + 1  + L$

带入原式:  $ dp[i]=dp[j]+a[i]^2-2a[i]*b[j]+b[j]^2$

          $dp[j]+b[j]^2   ( y )    =2a[i]b[j]  (kx)  + dp[i]-a[i]^2  (b)$

      

很明显  斜率为2a[i]  显然为递增的   所以只要维护一个递增的斜率即可  第一个 斜率$(j,j+1)>2a[i] $即为答案

同时  队列右端要维护最小值  因为是一个凸包

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i>=(b);--i)
#define ll long long
#define see(x) (cerr<<(#x)<<'='<<(x)<<endl)
#define inf 0x3f3f3f3f
#define CLR(A,v)  memset(A,v,sizeof A)
/////////////////////////////////////
const int N=1e5+10;
ll L,n,m,sum[N],dp[N];
ll a(int x){return sum[x]+x;}
ll b(int x){return sum[x]+x+1+L;}
ll X(int x){return b(x);}
ll Y(int x){return dp[x]+b(x)*b(x);}
double sp(int a,int b){return (Y(a)-Y(b))/(X(a)-X(b));}
int q[N],l,r;
int main()
{
    cin>>n>>L;
    rep(i,1,n)scanf("%lld",&sum[i]),sum[i]+=sum[i-1];
    l=r=1;
    rep(i,1,n)
    {
        while(l<r && sp(q[l],q[l+1])<2*a(i))l++;
        dp[i]=dp[q[l]]+(a(i)-b(q[l]))*(a(i)-b(q[l]));
        while(l<r && sp(q[r-1],q[r])>sp(q[r],i))r--;
        q[++r]=i;
    }
    cout<<dp[n];
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/bxd123/p/11585931.html