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

bzoj1010 [HNOI2008]玩具裝箱toy

輸入 tdi const noi2008 alt 最優 for time http

#[HNOI2008]玩具裝箱toy ####Time Limit: 1 Sec Memory Limit: 162 MB ###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,而且一眼就可以看出怎麽做。。。 設$f[i]$表示前$i$個木板的最優解,$sum[i]$表示前$i$個木板的長度和。 f[i]=Min(f[j]+(sum[i]-sum[j]+i-j-L-1)2) [0<=j<i] 然後又是看了一眼數據範圍,發現這個東西實在是有些暴力。所以我們要優化。。。。。 然後,這就是一道特別經典的斜率優化$dp$.

我們先把這個式子進行一波變形(假設$L++$)s[k]=sum[k]+k f[i]=f[j]+(s[i]-s[j]-L)^2

f[i]+2*s[i]*(s[j]+L)=f[j]+s[i]^2+(s[j]+L)^2\

為什麽要化為這個形式呢,我們可以觀察一下這個式子: 由於我們要求的是$f[i]$,這個式子中的變量為$j$,所以觀察可以發現這個式子其實是一個一次函數一般式$b+kx=y$ b=f[i]\ k=2*s[i]\ x=(s[j]+L)\ y=f[j]+s[i]^2+(s[j]+L)^2 而我們要幹的事情就是已知$k$,然後不斷的代入$(x,y)$,最後去解那個$b$. 所以我們用一張圖來表示需要我們去做的事

技術分享圖片

我們要用一條已知斜率的平行線由給定點的位置不斷的平移,然後求最小的截距。 這個操作其實就可以來進行優化了。 首先發現$s[i]$是單增的。 由上圖我們可以發現,假設直線$A,B$的斜率小於$k$,那麽$A$就一定不可能成為最優解(顯然無論什麽時候選$B$比它更優,($k$是單增的)) 所以我們先這個從頭開始一波刪點。 再考慮加了當前點以後。

技術分享圖片

顯然如果新的點比前面的點更優,那麽我們也一定選擇新的點。 總的看很像在維護一個凸包。。。。。 這就是最簡單的斜率優化啦~~~

那麽我們來總結一下什麽時候可以用最經典的斜率優化: 首先要有單調性。 可以寫成一次函數的形式,而且變量是一個點並且已知斜率,或者說你要求的答案剛好可以表示成截距。 個人認為斜率優化實在充分理解$dp$狀態轉移方程以後得到的。如果你遇到了一個狀態轉移方程並且需要優化的話,你可以嘗試能否滿足上面兩種要求。


#include<cstdio>
#define ll long long
#define Empty (head>=tail)
#define go(i,a,b) for(int i=a;i<=b;i++)
const int N=50100;ll s[N],Q[N],f[N],n,x,head,L,tail,j;
inline double X(ll i){return s[i];}
inline double Y(ll i){return f[i]+(s[i]+L-1)*(s[i]+L-1);}
inline double Rate(ll i,ll k){return (Y(k)-Y(i))/(X(k)-X(i));}
int main()
{
    scanf("%lld%lld",&n,&L);s[0]=0;L++;head=1;tail=1;Q[1]=0;
    go(i,1,n){scanf("%lld",&s[i]);s[i]+=s[i-1];}go(i,1,n)s[i]+=i;
    go(i,1,n)
    {
        while(!Empty&&Rate(Q[head],Q[head+1])<2*s[i])head++;
        j=Q[head];f[i]=f[j]+(s[i]-s[j]-L)*(s[i]-s[j]-L);
        while (!Empty&&Rate(Q[tail-1],Q[tail])>Rate(Q[tail],i))tail--;Q[++tail]=i;
    }
    printf("%lld\n",f[n]); return 0;
}

bzoj1010 [HNOI2008]玩具裝箱toy