【題意】
給出n條連續線段,每條線段都有長度爲x[i],咱們能夠把連續若干條線段連在一塊兒,變成一個組合,兩條線段若是相連,就要在兩條線段中間添加一個長度爲1的格子(若是沒有相連就不用添加),假如咱們如今選擇把第i條到第j條線段之間的全部線段變成一組合的話,這個組合的總長度就爲:x[i]+x[i+1]+x[i+2]+x[i+3]+...+x[j]+j-i,如今給出一個常數L,假設當前選擇的組合的長度爲s,那麼這個組合就爲咱們產生了(s-L)^2的費用,求出把n條線段分紅若干組合所須要的最小費用,單獨的線段能夠成爲一個組合
【輸入文件】
第一行兩個整數,分別爲N和L。
下來N個數字xi,按編號從小到大輸入每一個物品的容量。
1<=N<=50000,1<=L,xi<=10^7
【輸出文件】
一個整數,總花費的最小值。
【樣例輸入】
5 4
3 4 2 1 4
【樣例輸出】
1
php
斜率優化其實就是一個用來優化DP的算法,但必須當DP方程具備單調性的時候才能使用算法
下來咱們用例題來講明斜率優化函數
f[i]表示1~i的最小花費。
f[i]=min(f[j]+(sum[i]-sum[j]+i-(j+1)-L)^2) (j<i)
f[i]=min(f[j]+(sum[i]+i-sum[j]-j-1-L)^2) (j<i)
令s[i]=sum[i]+i,L=1+L
則f[i]=min(f[j]+(s[i]-s[j]-L)^2)優化
首先咱們先來證實決策單調性
假設j1<j2<i,在狀態i處的j2決策不比j1決策差(內心想着淘汰j1),
即要知足:f[j2]+(s[i]-s[j2]-L)^2<f[j1]+(s[i]-s[j1]-L)^2
則對於i後的全部狀態t,是否j2也不比就j1差?(術語:證實決策單調性)
即f[j2]+(s[t]-s[j2]-L)^2 < f[j1]+(s[t]-s[j1]-L)^2
容易理解s[t]=s[i]+v
因此獲得(1)不等式:f[j2]+(s[i]-s[j2]-L+v)^2<f[j1]+(s[i]-s[j1]-L+v)^2
由於已知(2)不等式:f[j2]+(s[i]-s[j2]-L)^2<f[j1]+(s[i]-s[j1]-L)^2
因此化簡(1)不等式:把s[i]-s[j2]-L當作一個總體,v當作一個總體,獲得:
f[j2]+(s[i]-s[j2]-L)^2+2*v*(s[i]-s[j2]-L)+v^2 <f[j1]+(s[i]-s[j1]-L)^2+2*v*(s[i]-s[j1]-L)+v^2spa
比較(2)不等式:
左邊多了一部分:2*v*(s[i]-s[j2]-L)+v^2
右邊多了一部分:2*v*(s[i]-s[j1]-L)+v^2
因此咱們只須要證:
2*v*(s[i]-s[j2]-L)+v^2<=2*v*(s[i]-s[j1]-L)+v^2
即:(s[i]-s[j2]-L)<=(s[i]-s[j1]-L)
即: -s[j2] <= -s[j1]
即:s[j1]<s[j2]這是確定的,因此得證。
總結:對於當前i:j2比j1好,那麼對於t(i<t)來講同樣:j2同樣比j1好,
因此當前i選擇j2,淘汰j1,之後的t也不會在j2存在的時候選擇j1
因此i的時候就能夠永久淘汰j1code
而後來求斜率方程
由於f[j2]+(s[i]-s[j2]-L)^2<=f[j1]+(s[i]-s[j1]-L)^2
展開:
f[j2]+(s[i]-L)^2-2*(s[i]-L)*s[j2]+s[j2]^2<=f[j1]+(s[i]-L)^2-2*(s[i]-L)*s[j1]+s[j1]^2
即f[j2]-2*(s[i]-L)*s[j2]+s[j2]^2<=f[j1]-2*(s[i]-L)*s[j1]+s[j1]^2
即f[j2]+s[j2]^2-2*(s[i]-L)*s[j2]<=f[j1]+s[j1]^2-2*(s[i]-L)*s[j1]
即[(f[j2]+s[j2]^2)-(f[j1]+s[j1]^2)]<=2*(s[i]-L)*s[j2]-2*(s[i]-L)*s[j1]
即[(f[j2]+s[j2]^2)-(f[j1]+s[j1]^2)]/(s[j2]-s[j1])<=2*(s[i]-L)
對於j來講:
製造的點座標
Y=f[j]+s[j]^2
X=s[j]
咱們用隊列list在存有意義的決策點,list中相鄰兩點的斜率遞增(隊列中的點造成一個下凸殼),並且都大於2*(s[i]-L),那麼隊列頭對於i來講就是最優決策點
加入決策i時,令隊尾爲list[tail],前一個爲list[tail-1]blog
斜率函數slop(點1,點2)
知足:slop(list[tail-1],list[tail])>slop(list[tail],i)時,
那麼隊尾list[tail]在三者(list[tail-1],list[tail],i)對於將來的tail絕對不會是最優的策略,因此將其彈出tail--
最後遇到了:slop(list[tail-1],list[tail])<slop(list[tail],i),保證了隊列的相鄰兩點的斜率遞增因此加入i:list[++tail]=i;隊列
而後f[n]就是答案了get
#include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> using namespace std; typedef long long LL; LL a[51000],s[51000],f[51000]; /* f[i]=min(f[j]+(sum[i]-sum[j]+i-j-1-L)^2) f[i]=min(f[j]+(s[i]-s[j]-L)^2) f[j2]+(s[i]-s[j2]-L)^2<=f[j1]+(s[i]-s[j1]-L)^2 f[j2]+(s[i]-L)^2-2*s[j2]*(s[i]-L)+s[j2]^2<=f[j1]+(s[i]-L)^2-2*s[j1]*(s[i]-L)+s[j1]^2 f[j2]+s[j2]^2-2*s[j2]*(s[i]-L)<=f[j1]+s[j1]^2-2*s[j1]*(s[i]-L) f[j2]-f[j1]+s[j2]^2-s[j1]^2<=2*s[j2]*(s[i]-L)-2*s[j1]*(s[i]-L) f[j2]-f[j1]+s[j2]^2-s[j1]^2<=2*s[j2]*(s[i]-L)-2*s[j1]*(s[i]-L) (f[j2]-f[j1]+s[j2]^2-s[j1]^2)/(s[j2]-s[j1])<=2*(s[i]-L) */ double slop(int j1,int j2) { return (f[j2]-f[j1]+s[j2]*s[j2]-s[j1]*s[j1])/(s[j2]-s[j1]); } int list[51000];int head,tail; int main() { LL L;int n; scanf("%d%lld",&n,&L);L++; for(int i=1;i<=n;i++) scanf("%lld",&a[i]); s[1]=a[1]+1; for(int i=2;i<=n;i++) s[i]=s[i-1]+a[i]+1; head=1;tail=1;list[1]=0; for(int i=1;i<=n;i++) { while(head<tail&&slop(list[head],list[head+1])<=2.0*(s[i]-L)) head++; int j=list[head]; f[i]=f[j]+(s[i]-s[j]-L)*(s[i]-s[j]-L); while(head<tail&&slop(list[tail],i)<slop(list[tail-1],list[tail])) tail--; list[++tail]=i; } printf("%lld\n",f[n]); return 0; }