1 条题解

  • 0
    @ 2025-10-8 17:01:58

    E53 斜率优化DP [HNOI2008]玩具装箱

    /*
    这是100分代码 
    f[i]表示包装好第1至第i个物品的最小花费(不一定只用一个包装盒,只要1-i包装好就行)
    
    f[i]=min(f[j]+(sa[i]-sa[j]+i-(j+1)-L)^2) (j<i)
    f[i]=min(f[j]+(sa[i]+i-sa[j]-j-1-L)^2) (j<i)
    令s[i]=sa[i]+i,L=1+L
    则f[i]=min(f[j]+(s[i]-s[j]-L)^2)
    
    f[i]=f[j]+(s[i]-s[j]-L)^2)
    f[i]=f[j]+s[i]^2+s[j]^2+L^2-2*s[i]*s[j]-2*L*s[i]+2*L*s[j] 
    
    2*s[i]*s[j] +f[i]-s[i]^2-L^2+2*L*s[i]=f[j]+s[j]^2+2*L*s[j] 
    kx + b = y
    k=2*s[i],x=s[j],b=f[i]-s[i]^2-L^2+2*L*s[i]
    y=f[j]+s[j]^2+2*L*s[j] 
    
    */ 
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=55500;
    LL a[N],s[N],f[N],L;
    int q[N];
    double X(int j){return 2.0*s[j];}
    double Y(int j){return 1.0*(f[j]+s[j]*s[j]+2.0*L*s[j]);}
    double slop(int j1,int j2){return X(j2)==X(j1)?  1e18*( Y(j2)-Y(j1) )  :( Y(j2)-Y(j1) ) / ( X(j2)-X(j1) );}
    int  main()
    {
        int n;scanf("%d%lld",&n,&L);
        s[0]=0;for(int i=1;i<=n;i++) scanf("%d",&a[i]),s[i]=s[i-1]+a[i];
        for(int i=1;i<=n;i++) s[i]+=i;
        L++;
        int l=1,r=1;q[1]=0;f[0]=0;
        for(int i=1;i<=n;i++)
        {
            while( (l<r)&&( slop(q[l],q[l+1])<=s[i] ) )l++;
            f[i]=f[q[l]]+(s[i]-s[q[l]]-L)*(s[i]-s[q[l]]-L);
            while( (l<r)&&( slop(q[r-1],q[r])>=slop(q[r],i) ) )r--;
            q[++r]=i;
        }
        printf("%lld\n",f[n]);
        return  0;
    }
    /*
    
    第二种解释(不推荐):
     
    1.设 j1 < j2 < i ,且j2 比 j1 好,则:f[j1]+(s[i]-s[j1]-L)^2 > f[j2]+(s[i]-s[j2]-L)^2
    展开:
    f[j1]+(s[i]-L)^2-2*(s[i]-L)*s[j1]+s[j1]^2 > f[j2]+(s[i]-L)^2-2*(s[i]-L)*s[j2]+s[j2]^2
    f[j1]-2*(s[i]-L)*s[j1]+s[j1]^2 > f[j2]-2*(s[i]-L)*s[j2]+s[j2]^2
    f[j1]+s[j1]^2-2*(s[i]-L)*s[j1] > f[j2]+s[j2]^2-2*(s[i]-L)*s[j2]
    [ (f[j1]+s[j1]^2) -(f[j2]+s[j2]^2)] >  2*(s[i]-L)*s[j1]-2*(s[i]-L)*s[j2]
    [ (f[j1]+s[j1]^2) -(f[j2]+s[j2]^2)] >  2*(s[i]-L)*(s[j1]-s[j2])
    因为 (s[j1]-s[j2])<0 
    [ (f[j1]+s[j1]^2)-(f[j2]+s[j2]^2) ] /(s[j1]-s[j2]) <  2*(s[i]-L)
    注意:因为 2*(s[i]-L) 会随着i增加越来越大 
    
    结论1:j1<j2<i,且j2比j1好,j1和j2的斜率小于 i相关的固定值2*(s[i]-L) ,
    因为这个固定值随i增加而增加,故j2可以永久淘汰j1。
    
    结论2: 维护队列下凸
    上凸删除队尾的理由分三种:
    1、将来t>i给出的斜率大于队尾上凸两个斜率(即大于当前队列所有斜率),那么队头开始删除直到只有一个点
    2、将来t>i给出的斜率介于队尾上凸两个斜率之间,则队尾q[r]比q[r-1]和i都差,也应该删除q[r]
    3、将来t>i给出的斜率小于队尾上凸两个斜率,则q[r-1]比q[r]和i都好,可以删除q[r] 
    在上凸的前提下,以上3种情况q[r]都无法是3点(q[r-1]、q[r]、i)中最优的,所以q[r] 可以删除 
    
    最后遇到了:slop(q[r-1],q[r])>slop(q[r],i) , 保证了队列的相邻两点的斜率递
    增所以加入i: q[++r]=i;
    
    
    #include<bits/stdc++.h>//60分
    using namespace std;
    typedef long long LL;
    LL a[55500],sa[55500],f[55500];
    int main()
    {
        LL L;int n;scanf("%d%lld",&n,&L);
        sa[0]=0;
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),sa[i]=sa[i-1]+a[i];
        f[0]=0;
        for(int i=1;i<=n;i++)
        {
            f[i]=(LL)1<<60;//f[i-1]+(a[i]-L)*(a[i]-L);
            for(int j=i-1;j>=0;j--)
            {
                f[i]=min(f[i],f[j]+(sa[i]-sa[j]+i-(j+1)-L)*(sa[i]-sa[j]+i-(j+1)-L));
            }
        }
        printf("%lld\n",f[n]);
        return 0;
    } 
    
    */
    
    • 1

    E53【斜率优化】[HNOI2008] 玩具装箱

    信息

    ID
    2663
    时间
    100ms
    内存
    125MiB
    难度
    5
    标签
    递交数
    67
    已通过
    25
    上传者