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

来源:互联网 发布:淘宝手机p图教程视频 编辑:程序博客网 时间:2024/06/05 06:01

题目描述

分析:

题目一看就是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 50000typedef 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
原创粉丝点击