1. 程式人生 > >bzoj1010[HNOI2008]玩具裝箱toy

bzoj1010[HNOI2008]玩具裝箱toy

con blank str 要去 scan efi 般的 希望 div

傳送門

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

Sample Output

1

題解

這道題是一道dp,需要采用斜率優化。一般的dp方程很好寫出:記dp[i]為前i個物品的最小代價,s[i]為前i中物品的總長度。則有dp[i]=min(d[i],dp[j]+(s[i]-s[j]+i-j-1-L)^2) (j<i)。

我們記f[i]=sum[i]+i,cc=l+1.

則有dp[i]=min(dp[i],dp[j]+(f[i]-f[j]-cc)^2)。

顯然f[i]是單調遞增的。斜率為(dp[k]+(f[k]+c)^2-dp[j]-(f[j]+c)^2)/2*(f[k]-f[j])<=f[i]

我們用隊列維護,每次取出隊頭。

隊尾為q[tail],前一個為q[tail-1]。

滿足斜率(qtail],i)<斜率(q[tail-1],q[tail])時,隊尾無效,將其彈出。

代碼

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<algorithm>
 6
#include<cmath> 7 #define ll long long 8 using namespace std; 9 int n,l,head,tail; 10 int c[50010],q[50010]; 11 ll s[50010],f[50010],cc; 12 double xie(int x,int y){ 13 return (f[y]-f[x]+(s[y]+cc)*(s[y]+cc)-(s[x]+cc)*(s[x]+cc))/(2.0*(s[y]-s[x])); 14 } 15 void dp(){ 16 int i,j; 17 head=tail=1;q[1]=0; 18 for(i=1;i<=n;++i){ 19 while(head<tail && xie(q[head],q[head+1])<=s[i]) head++; 20 int x=q[head]; 21 f[i]=f[x]+(s[i]-s[x]-cc)*(s[i]-s[x]-cc); 22 while(head<tail && xie(q[tail],i)<xie(q[tail-1],q[tail])) tail--; 23 q[++tail]=i; 24 } 25 } 26 int main(){ 27 int i,j; 28 scanf("%d%d",&n,&l);cc=l+1; 29 for(i=1;i<=n;++i){ 30 scanf("%d",&c[i]); 31 } 32 s[0]=0; 33 for(i=1;i<=n;++i) s[i]=s[i-1]+c[i]; 34 for(i=1;i<=n;++i) s[i]+=i; 35 dp(); 36 printf("%lld\n",f[n]); 37 return 0; 38 39 }

bzoj1010[HNOI2008]玩具裝箱toy