1. 程式人生 > >BZOJ 1010 HNOI2008 玩具裝箱 斜率優化

BZOJ 1010 HNOI2008 玩具裝箱 斜率優化

接下來 趨勢 得到 double php 只有一個 body IT AR

題目鏈接:

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

5 4
3
4
2
1
4

Sample Output

1

HINT

1<=N<=50000,1<=L,Ci<=10^7 ————————————————————————————————————————————————

題意概述:

·現在有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 }

BZOJ 1010 HNOI2008 玩具裝箱 斜率優化