【斜優DP】bzoj4518-Sdoi2016征途

1、斜率優化DP與決策單調性

這裏淺顯(而且不嚴謹)地說明一下標題中的兩個名詞:php

斜率優化DP:狀態轉移方程形如f[i]=min/max{f[k]+(x[i]-x[k])^y}的一類DP問題;html

決策單調性:若對於狀態i,有決策t<k,且k優於t,則對於任意狀態v>i,存在決策k優於t。數組

對以上兩條說明的不嚴謹與模糊之處,下文將結合題目給出(依舊不嚴謹且模糊的)必定程度上的解釋。ide

2、題目

Description

Pine開始了從S地到T地的征途。
從S地到T地的路能夠劃分紅n段,相鄰兩段路的分界點設有休息站。
Pine計劃用m天到達T地。除第m天外,每一天晚上Pine都必須在休息站過夜。因此,一段路必須在同一天中走完。
Pine但願每一天走的路長度儘量相近,因此他但願每一天走的路的長度的方差儘量小。
幫助Pine求出最小方差是多少。
設方差是v,能夠證實,v×m^2是一個整數。爲了不精度偏差,輸出結果時輸出v×m^2。

Input

第一行兩個數 n、m。
第二行 n 個數,表示 n 段路的長度

Output

 一個數,最小方差乘以 m^2 後的值優化

Sample Input

5 2
1 2 5 8 6

Sample Output

36

HINT

1≤n≤3000,保證從 S 到 T 的總路程不超過 30000spa

附上原題連接→_→Problem 4518. -- [Sdoi2016]征途設計

3、題目分析

爲方便書寫,做出以下約定:3d

a[i]:題目給出的第i段路程長;code

sum[i]:路程長的前綴和;htm

x[i]:第i天走過的路程總和。

根據題意,天天走過的路程長度的方差表示爲:

整理得:

顯然,因爲上式中只有x[i]爲變量,問題天然轉化成了求∑x2[i]的最小值。

設計狀態轉移方程以下:

其中,f[i][j]表示前j天走過前i段路程,天天路程平方和的最小值。

觀察到狀態轉移方程的形式基本符合斜率優化DP的形式,因而固定j,並:

令t<k:

當t優於k:

整理得:

同理,當k優於t:

因爲s[i]單調遞增,故若在狀態i下有k優於t,則對於任意v>i,存在k優於t。

這就意味着,若是咱們在某個狀態時發現決策k優於t,咱們便不再須要訪問決策t了。這就利用決策單調性,達到了下降時間複雜度的效果。

此外,還能夠發現,在某個狀態下的有效決策造成了一個下凸殼。粗略證實以下:

設座標系中存在點A、B、C,其橫座標單調遞增,其對應決策簡稱爲決策A、B、C

當B的縱座標大於A、C的縱座標(造成了一個上凸殼):

  1. 2s[i]<kBC:決策A優於決策B,決策B優於決策C,故決策A最優;
  2. kBC<2s[i]<kAB:決策A優於決策B,決策C優於決策B,故決策A或C最優;
  3. 2s[i]>kAB:決策B優於決策A,決策C優於決策B,故決策C最優。

以上,造成上凸殼時,決策B不可能成爲最優決策,故刪去點B,上凸殼性質被破壞;

同理,當B的縱座標小於A、C的縱座標(造成了一個下凸殼):

  1. 2s[i]<kAB:決策A優於決策B,決策B優於決策C,故決策A最優;
  2. kAB<2s[i]<kBC:決策B優於決策A,決策B優於決策C,故決策B最優;
  3. 2s[i]>kBC:決策B優於決策A,決策C優於決策B,故決策C最優。

故造成下凸殼時,全部決策均有可能成爲最優決策。

這樣,咱們用一個雙端隊列就能維護可能成爲最優決策的的決策,始終保證隊首元素最優,隊列知足下凸殼性質。

4、代碼實現

其實f數組不須要開二維,由於咱們每次只用到了f[i][j]和f[i][j-1],因此能夠再壓去一維。但爲方便理解,博主仍使用二維f數組。

 1 #include<cstdio>
 2 const int MAXN=3e3+10;
 3 int n,m;
 4 int s[MAXN];
 5 int q[MAXN],l,r;
 6 long long f[MAXN][MAXN];
 7 double count_y(int k,int j){return f[k][j-1]+s[k]*s[k];}
 8 double count(int t,int k,int j){return (count_y(t,j)-count_y(k,j))/(s[t]-s[k]);}
 9 int main()
10 {
11     scanf("%d%d",&n,&m);
12     for(int i=1;i<=n;++i)
13     {
14         int x;
15         scanf("%d",&x);
16         s[i]=s[i-1]+x;
17     }
18     for(int i=1;i<=n;++i)f[i][1]=s[i]*s[i];
19     for(int j=2;j<=m;++j)
20     {
21         l=1,r=1;
22         for(int i=1;i<=n;++i)
23         {
24             while(l<r&&count(q[l],q[l+1],j)<2*s[i])++l;
25             int temp=q[l];
26             f[i][j]=f[temp][j-1]+(s[i]-s[temp])*(s[i]-s[temp]);
27             while(l<r&&count(q[r],i,j)<count(q[r-1],q[r],j))--r;
28             q[++r]=i;
29         }
30     }
31     printf("%lld\n",f[n][m]*m-(long long)s[n]*s[n]);
32     return 0;
33 }
bzoj4518-征途

弱弱地說一句,本蒟蒻碼字也不容易,轉載請註明出處http://www.cnblogs.com/Maki-Nishikino/p/6523852.html

相關文章
相關標籤/搜索