1010: [HNOI2008]玩具装箱toy(斜率优化dp)

斜率优化真他妈好玩

原题: https://www.lydsy.com/JudgeOnline/problem.php?id=1010

题意:

1…N的N件玩具长度为Ci。分成若干段。每段的长度将为 x = j i + k = i j C k x=j-i+\sum_{k=i}^jCk
长度为x,其制作费用为(X-L)^2.其中L是一个常量。求费用的最小值。

解析

递推式: d p [ i ] = d p [ j ] + ( i j 1 + s u m i s u m j L ) 2 dp[i]=dp[j]+(i-j-1+sum_i-sum_j-L)^2

对于两点 j , k ( j < k ) j,k(j<k) ,使k的值更优得: d p [ j ] + ( i j 1 + s u m i s u m j L ) 2 > d p [ k ] + ( i k 1 + s u m i s u m k L ) 2 dp[j]+(i-j-1+sum_i-sum_j-L)^2>dp[k]+(i-k-1+sum_i-sum_k-L)^2 d p [ j ] + ( j + s u m j ) 2 2 ( i + s u m i L 1 ) ( j + s u m j ) > d p [ k ] + ( k + s u m k ) 2 2 ( i + s u m i L 1 ) ( k + s u m k ) dp[j]+(j+sum_j)^2-2(i+sum_i-L-1)(j+sum_j)>dp[k]+(k+sum_k)^2-2(i+sum_i-L-1)(k+sum_k) d p [ j ] d p [ k ] + ( j + s u m j ) 2 ( k + s u m k ) 2 > 2 ( i + s u m i L 1 ) ( j + s u m j ( k + s u m k ) ) dp[j]-dp[k]+(j+sum_j)^2-(k+sum_k)^2>2(i+sum_i-L-1)(j+sum_j-(k+sum_k)) d p [ j ] d p [ k ] + ( j + s u m j ) 2 ( k + s u m k ) 2 j + s u m j ( k + s u m k ) < 2 ( i + s u m i L 1 ) \dfrac{dp[j]-dp[k]+(j+sum_j)^2-(k+sum_k)^2}{j+sum_j-(k+sum_k)}<2(i+sum_i-L-1)

验证正确性并得出单调性:

首先因为是小于号,所以队列中值为大于 2 ( i + s u m i L 1 ) 2(i+sum_i-L-1) 的数。

而右侧值随着i变大而变大,在变大后,可能队首的值不符合要求要弹出(小于 2 ( i + s u m i L 1 ) 2(i+sum_i-L-1)

结论:

化解过程正确,单调性为:增;队列中的值大于 2 ( i + s u m i L 1 ) 2(i+sum_i-L-1)

细节:

由于第二个点也可以从0转移,所以需要压入0这个状态。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int maxn=50009;

LL n,L;
LL a[maxn];
LL dp[maxn];
LL sum[maxn];
int Q[maxn],l=1,r=1;

double check(int j,int k){
    return double(dp[j]-dp[k]+(j+sum[j])*(j+sum[j])-(k+sum[k])*(k+sum[k]))/(double)((j+sum[j])-(k+sum[k]));
}

int main(){
    scanf("%lld%lld",&n,&L);
    for(int i=1;i<=n;i++){
        scanf("%d",a+i);
        sum[i]=sum[i-1]+a[i];
    }
    dp[0]=0;
    Q[1]=0;

    for(int i=1;i<=n;i++){
        while(r>l&&check(Q[l],Q[l+1])<2*(i-L+sum[i]-1))l++;
        int j=Q[l];
        dp[i]=dp[j]+(i-j-1+sum[i]-sum[j]-L)*(i-j-1+sum[i]-sum[j]-L);
        //printf("%d -> %d dp[%d]=%lld\n",j,i,i,dp[i]);
        while(r>l&&check(Q[r-1],Q[r])>check(Q[r],i))r--;
        Q[++r]=i;
    }
    printf("%lld\n",dp[n]);
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/90602786