這裏淺顯(而且不嚴謹)地說明一下標題中的兩個名詞: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
一個數,最小方差乘以 m^2 後的值優化
1≤n≤3000,保證從 S 到 T 的總路程不超過 30000spa
附上原題連接→_→Problem 4518. -- [Sdoi2016]征途設計
爲方便書寫,做出以下約定: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的縱座標(造成了一個上凸殼):
以上,造成上凸殼時,決策B不可能成爲最優決策,故刪去點B,上凸殼性質被破壞;
同理,當B的縱座標小於A、C的縱座標(造成了一個下凸殼):
故造成下凸殼時,全部決策均有可能成爲最優決策。
這樣,咱們用一個雙端隊列就能維護可能成爲最優決策的的決策,始終保證隊首元素最優,隊列知足下凸殼性質。
其實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 }
弱弱地說一句,本蒟蒻碼字也不容易,轉載請註明出處http://www.cnblogs.com/Maki-Nishikino/p/6523852.html