BZOJ 1010 HNOI2008 玩具装箱 斜率优化
题目链接:
http://www.lydsy.com/JudgeOnline/problem.php?id=1010
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。
Output
输出最小费用
Sample Input
3
4
2
1
4
Sample Output
HINT
题意概述:
现在有N件物品,每件物品有一个长度Ci,现在要求把这些物品分组,每组必须是连续的一段,假如把第i~j件物品分成一组,那么这一组的长度x为j-i+sum{ck|i<=k<=j},同时这一组的代价为(x-L)^2,L是一个常量。现在问将这些物品分组的最小代价。1<=N<=50000,1<=L,Ci<=10^7.
分析:
容易看出来一个dp模型。
令f(i)表示将前i个物品分组的最小代价。
f(i)=min{ f(j)+(i-j-1+sum[i]-sum[j]-L)^2 | 0<=j<i }
令wi=i+sum[i],LL=L+1,去掉min,改写式子得到:
[2*wj*LL+wj^2+f(j)]=wi*(2*wj)+f(i)-(wi-LL)^2
如果把(2*wj,2*wj*LL+wj^2+f(j))看成点,那么现在要做的就是在一个点集中找到一个点使得f(i)-(wi-LL)^2最小。
每次计算的直线的斜率有单调递增的趋势。因为是斜率始终大于0并且要让纵截距最小,于是我们需要维护一个下凸壳。因为斜率具有单调性,所以说每一次计算的时候都从队首取出一个元素计算,并且和队首后面的元素计算出来的答案比较。如果队首的答案更劣,那么直接出队,因为斜率具有单调性,之后一定也不会用到这个点了。每一次计算完之后插入新点,对于新点来说从队尾开始看起。如果这个点和队尾前一个点的斜率小于队尾和队尾前一个点的斜率,那么说明队尾的点被包住了,出队,最后把这个点甩进去(因为插入的点的横坐标都是单调递增的,所以说不会有一些奇奇怪怪的问题)。两个出队操作都在当前点不更加优秀或者队列中只有一个点的时候停止。
时间复杂度O(N)。
注意两个很sb的问题:1.初始化的时候要用0来初始化,表示这个物品和前面所有的物品分成一组;2.因为我们引用了斜率这个概念,所以在推式子的时候一定记得把式子写成斜截式!!!
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<queue> 7 #include<set> 8 #include<map> 9 #include<vector> 10 #include<cctype> 11 using namespace std; 12 const int maxn=50005; 13 typedef long long LL; 14 15 int N,L,C[maxn]; 16 LL f[maxn],sum[maxn]; 17 struct XY{ LL x,y; }mq[maxn]; int front,rear; 18 19 void data_in() 20 { 21 scanf("%d%d",&N,&L); 22 for(int i=1;i<=N;i++) scanf("%d",&C[i]); 23 } 24 LL X(int i){ return 2*(i+sum[i]); } 25 LL Y(int i){ return 2*(i+sum[i])*(L+1)+(i+sum[i])*(i+sum[i])+f[i]; } 26 double getk(const XY &a,const XY &b){ return 1.0*(a.y-b.y)/(a.x-b.x); } 27 void work() 28 { 29 for(int i=1;i<=N;i++) sum[i]=sum[i-1]+C[i]; 30 mq[rear++]=(XY){X(0),Y(0)}; 31 XY p; 32 for(int i=1;i<=N;i++){ 33 while(rear-front>1&&getk(mq[front],mq[front+1])<i+sum[i]) front++; 34 f[i]=-(i+sum[i])*mq[front].x+mq[front].y+(i+sum[i]-L-1)*(i+sum[i]-L-1); 35 p=(XY){X(i),Y(i)}; 36 while(rear-front>1&&getk(p,mq[rear-2])<getk(mq[rear-1],mq[rear-2])) rear--; 37 mq[rear++]=p; 38 } 39 cout<<f[N]<<'\n'; 40 } 41 int main() 42 { 43 data_in(); 44 work(); 45 return 0; 46 }