HYSBZ/BZOJ 1010 [HNOI2008] 玩具装箱 - dp&斜率优化

题目描述

分析:

题目一看就是dp题。
dp[i] :前i个玩具所需的最小cost
sum[i] :c[i]的前缀和

dp[i]=min{dp[j]+(sum[i]sum[j1]+ijL)21<=j<=i}

复杂度O( n2 ),肯定要优化
由于牵扯到sum[j-1],而不是sum[j],将方程改写为:
dp[i]=min{dp[j]+(sum[i]sum[j]+i(j+1)L)2}0<=j<i

为了方便表示,记:
f[i] = sum[i] + i , c=1+L
则:
dp[i]=min{dp[j]+(f[i]f[j]c)2}0<=j<i

——————————————————————————————————————————

STEP 1:证明较优状态决策点对后续状态的持续影响 (就是说:对于当前状态i,有决策点k优于j,那么必定的,对于i之后的某个状态T,仍有决策点k优于j)

假设对于当前状态i,有决策点k优于j ,j < k
则:

dp[k]+(f[i]f[k]c)2<=dp[j]+(f[i]f[j]c)2

又因为f(x)是递增的,设 f[T]=f[i]+t
要证:
dp[k]+(f[T]f[k]c)2<=dp[j]+(f[T]f[j]c)2

要证:
dp[k]+(f[i]+tf[k]c)2<=dp[j]+(f[i]+tf[j]c)2

要证:
dp[k]+(f[i]f[k]c)2+2t(f[i]f[k]c)+t2<=dp[j]+(f[i]f[j]c)2+2t(f[i]f[j]c)+t2

要证:
2t(f[i]f[k]c)<=2t(f[i]f[j]c)

有结合f(x)的单调递增性,即可证明。

——————————————————————————————————————————

STEP 2:找斜率方程

由 (此时k优于j)

dp[k]+(f[i]f[k]c)2<=dp[j]+(f[i]f[j]c)2

推得:
dp[k]+(f[k]+c)2(dp[j]+(f[j]+c)2)<=2(f[k]f[j])f[i]

G(k,j)=dp[k]+(f[k]+c)2(dp[j]+(f[j]+c)2)

S(k,j)=2(f[k]f[j])

则:
K(k,j)=G(k,j)S(k,j)<=f[i]

——————————————————————————————————————————

Solution:

分析了那么久,累死了。
斜率优化用一个队列维护。
维护原则看代码吧,提示:que[]中的pos按大小顺序排。

——————————————————————————————————————————

#include<cstdio>
#define MAXN 50000
typedef long long LL;

int n,c,L;
LL sum[MAXN+10],f[MAXN+10],dp[MAXN+10],que[MAXN+10];
void read()
{
    int x;
    scanf("%d%d",&n,&L);
    for(int i=1;i<=n;i++){
        scanf("%d",&x);
        sum[i]=sum[i-1]+x;
    }
    for(int i=1;i<=n;i++)
        f[i]=sum[i]+i;
    c=L+1;
}
LL G(int k,int j){
    return (dp[k]+(f[k]+c)*(f[k]+c)) - (dp[j]+(f[j]+c)*(f[j]+c));
}
LL S(int k,int j){
    return 2*(f[k]-f[j]);
}
void DP()
{
    int front=0,rear=1,now;//que[0]=0,以便算出f[1]
    for(int i=1;i<=n;i++){
        while(front<rear-1){
            if(G(que[front+1],que[front])<=S(que[front+1],que[front])*f[i])
                front++;
            else
                break;
        }
        now=que[front];
        dp[i]=dp[now]+(f[i]-f[now]-c)*(f[i]-f[now]-c);
        while(front<rear-1){
            if(G(i,que[rear-1])*S(que[rear-1],que[rear-2])<=G(que[rear-1],que[rear-2])*S(i,que[rear-1]))
                rear--;
            else
                break;
        }
        que[rear++]=i;
    }
}
int main()
{
    read();
    DP();
    printf("%I64d\n",dp[n]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值