1. 程式人生 > >解題:HNOI 2008 玩具裝箱

解題:HNOI 2008 玩具裝箱

題面

搞了一晚上斜率優化,大概懂了一點,寫寫

原來常用的優化dp方法:做字首和,預處理,資料結構維護

現在有轉移方程長這樣的一類dp:$dp[i]=min(dp[i],k[i]*x[j]+y[j]+c[i]+a)$,其中$c[i],k[i],x[j],y[j]$都是關於$i$或者$j$的變數,在$i$或者$j$確定時不變,$a$是個常量

然後發現$x[j]$帶著一個$k[i]$的係數,不好優化

從另一個角度考慮,想想高中老師教給我們的線性規劃

可以發現因為對於每次轉移的$i$來說$c[i],k[i]$都不變,我們可以把$k[i]*x[j]+y[j]$看做是一條直線(初中的一次函式),$k[i]$是斜率,然後$x[j]$是橫座標,$y[j]$是縱座標,別的都是關於$i$的變數或者常量,不用管。那麼實際上我們在求這條直線的截距的最值(初中的與y軸的交點)

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=50005;
 6 long long len[N],dp[N],que[N],n,x,f,b;
 7 inline long long c1(int p){return len[p]+p;}
 8 inline long long c2(int p){return c1(p)+x+1;}
 9 inline long long K(int
p){return 2*c1(p);} 10 inline long long X(int p){return c2(p);} 11 inline long long Y(int p){return c2(p)*c2(p)+dp[p];} 12 inline long long S(int a,int b){return (double)(Y(b)-Y(a))/(double)(X(b)-X(a));} 13 int main() 14 { 15 scanf("%lld%lld",&n,&x); 16 for(int i=1;i<=n;i++) 17 scanf("
%lld",&len[i]),len[i]+=len[i-1]; 18 que[f=b=0]=0; 19 for(int i=1;i<=n;i++) 20 { 21 while(b-f>=1&&S(que[f],que[f+1])<K(i)) f++; 22 dp[i]=dp[que[f]]+(c1(i)-c2(que[f]))*(c1(i)-c2(que[f])); 23 while(b-f>=1&&S(que[b-1],i)<S(que[b],que[b-1])) b--; que[++b]=i; 24 } 25 printf("%lld",dp[n]); 26 return 0; 27 }
View Code