bzoj3156 斜率dp
入门的斜率dp,wa了真的好多发,,,
dp方程为dp[i]=dp[j]+a[i]+(i-j)*(i-j-1)/2;
基本正常思路设i>j>k,假设j比k优
dp[j]+(i-j)(i-j-1)/2<dp[k]+(i-k)*(i-k-1)/2;
化简得dp[j]-dp[k]+(j*j+j)/2-(k*k+k)/2<i*(j-k);
其实就是把j与k化简出来然后把i放在不等式另一边求斜率
得(yj-yk)/(xj-xk)<i;
然后就是斜率dp
那么yj-yk/xj-xk<i说明了什么呢? 我们前面是不是假设j的决策比k的决策要好才得到这个表示的? 如果是的话,那么就说明g[j,k]=yj-jk/xj-xk<i代表这j的决策比k的决策要更优。
关键的来了:现在从左到右,还是设k<j<i,如果g[i,j]<g[j,k],那么j点便永远不可能成为最优解,可以直接将它踢出我们的最优解集。为什么呢?
我们假设g[i,j]<sum[i],那么就是说i点要比j点优,排除j点。
如果g[i,j]>=sum[i],那么j点此时是比i点要更优,但是同时g[j,k]>g[i,j]>sum[i]。这说明还有k点会比j点更优,同样排除j点。
排除多余的点,这便是一种优化!
于是对于这题我们对于斜率优化做法可以总结如下:
1,用一个单调队列来维护解集。
2,假设队列中从头到尾已经有元素a b c。那么当d要入队的时候,我们维护队列的性质,即如果g[d,c]<g[c,b],那么就将c点删除。直到找到g[d,x]>=g[x,y]为止,并将d点加入在该位置中。
3,求解时候,从队头开始,如果已有元素a b c,当i点要求解时,如果g[b,a]<i,那么说明b点比a点更优,a点可以排除,于是a出队。
1 #include <cstdio> 2 #include <cstring> 3 #include <set> 4 #include <iostream> 5 #include <map> 6 #include <math.h> 7 #include <algorithm> 8 #include <vector> 9 using namespace std; 10 typedef long long ll; 11 ll a[1000005],dp[1000005]; 12 int q[1000005]; 13 ll qujian(int u) 14 { 15 ll i=u; 16 return (i-1)*(i)/2; 17 } 18 ll getup(int i1,int j1) 19 { 20 ll i=i1,j=j1; 21 return (2*dp[j1]+(j*j+j))-(2*dp[i1]+(i*i+i)); 22 } 23 ll getdown(int i1,int j1) 24 { 25 ll i=i1,j=j1; 26 return 2*(j-i); 27 } 28 int n; 29 int main() 30 { 31 // freopen("in.txt","r",stdin); 32 // cout<<"r"<<endl; 33 cin>>n; 34 int i; 35 36 // cout<<"r"<<endl; 37 for(i=1; i<=n; i++) 38 scanf("%lld",&a[i]); 39 40 //cout<<"r"<<endl; 41 // dp[1]=a[1]; 42 int head=0,tail=0; 43 q[tail]=0; 44 for(i=1; i<=n; i++) 45 { 46 while(head<tail&&getup(q[head],q[head+1])<i*getdown(q[head],q[head+1])) 47 head++; 48 //cout<<"r"<<endl; 49 dp[i]=dp[q[head]]+a[i]+qujian(i-q[head]); 50 while(head<tail&&getup(q[tail-1],q[tail])*getdown(q[tail],i)>getup(q[tail],i)*getdown(q[tail-1],q[tail])) 51 tail--; 52 q[++tail]=i; 53 } 54 cout<<dp[n]<<endl; 55 56 57 return 0; 58 }