这里浅显(而且不严谨)地说明一下标题中的两个名词: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