1010: [HNOI2008]玩具装箱toy
Description
P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为1...N的N件玩具,第i件玩具经过压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一个容器中,那么容器的长度将为 x=j-i+Sigma(Ck) i<=K<=j 制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为x,其制作费用为(X-L)^2.其中L是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过L。但他希望费用最小.
Input
第一行输入两个整数N,L.接下来N行输入Ci.1<=N<=50000,1<=L,Ci<=10^7
Output
输出最小费用
Sample Input
5 4
3
4
2
1
4
3
4
2
1
4
Sample Output
1
法1:O(n logn) 栈优化走起!
#include<cstdio> #define MaxN 50010 typedef long long LL; int N,L; LL A[MaxN],f[MaxN]; int st[MaxN],w,t,b[MaxN],e[MaxN]; LL pow(LL x){ return x*x; } LL calc(int x,int y){ return f[x]+pow(A[y]-A[x]-1-L); } int main(){ scanf("%d%d",&N,&L); for (int i=1;i<=N;++i) scanf("%lld",&A[i]); for (int i=1;i<=N;++i) A[i]+=A[i-1]+1; b[0]=0; e[ st[ w=t=0 ]=0 ]=N; for (int i=1;i<=N;++i){ while ( e[ st[w] ]<i ) ++w; b[ st[w] ]=i; f[i]=calc(st[w],i); if ( calc(st[t],N) < calc(i,N) ) continue; while ( w<=t && calc(st[t],b[st[t]]) >= calc(i,b[st[t]]) ) --t; int be=e[ st[t] ]+1,l=b[ st[t] ],r=e[ st[t] ]; while (l<=r){ int mid=(l+r)/2; if ( calc(st[t],mid) >= calc(i,mid) ){ be=mid; r=mid-1; } else l=mid+1; } e[ st[t] ]=be-1; st[++t]=i; b[i]=be; e[i]=N; } printf("%lld\n",f[N]); }
法2:O(n) 美妙的斜率优化。。。
蒟蒻wy表示写完整个人都斜率优化了。。。
#include<cstdio> #define MaxN 50010 typedef long long LL; int N,L,Q[MaxN],ql,qr; LL sum[MaxN],a[MaxN],b[MaxN],X[MaxN],Y[MaxN],f[MaxN]; LL calc(int x,int y){ return f[x]+b[x]*b[x]-2*a[y]*b[x]; } bool check(int p,int q,int o){ return (X[p]-X[o])*(Y[q]-Y[o])-(X[q]-X[o])*(Y[p]-Y[o])<=0; } int main(){ scanf("%d%d",&N,&L); for (int i=1;i<=N;++i) scanf("%lld",&sum[i]); for (int i=1;i<=N;++i){ sum[i]+=sum[i-1]; a[i]=sum[i]+i-L-1; b[i]=sum[i]+i; } for (int i=1;i<=N;++i){ while ( ql<qr && calc(Q[ql],i)>=calc(Q[ql+1],i) ) ++ql; f[i]=calc(Q[ql],i)+a[i]*a[i]; X[i]=b[i]; Y[i]=f[i]+b[i]*b[i]; while ( ql<qr && check(Q[qr-1],Q[qr],i) ) --qr; Q[++qr]=i; } printf("%lld\n",f[N]); }
Ps:以上两种解法详见 1D/1D动态规划优化初步