刚刚接触斜率优化DP,做了几道比较基础的题,算是稍微有些理解了。
斜率优化DP的资料很多,最经典的资料还是周源04年OI论文《浅谈数形结合思想在信息学竞赛中的应用》,初学可以看它,进阶的话可以再去看看杨哲的《凸完全单调性的一个加强与应用》,做题可以搜题解。
几点学习心得:
1.动态规划时间复杂度:阶段*状态*决策所需时间。阶段*状态一般可视为状态总数,一般状态总数是确定的,我们无法减少它的遍历。那么,斜率优化DP就是在决策前通过维护策略的单调性减少状态数,降低一次决策所需的时间,从而降低时间复杂度。单调性如何维护由如何决策而定。
2.斜率优化DP一般需要先写出容易想到的状态转移方程,然后列举2个子状态发现是否对后续状态有单调性。
3.斜率优化DP一般通过单调队列维护状态,数形结合有助于解题,一般维护状态的单调性就是在2维坐标系中维护下凸折线。
4.需要参与决策的状态才能够入队,否则不要加入队列,或延迟加入它。特别注意新加入状态斜率与队尾最后2个状态斜率相等的情况,舍取要看题意。
列3道基础斜率优化DP题:
1.HDU 3507 Print Article
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #define maxn 500005 3 typedef long long ll; 4 long long dp[maxn]; 5 ll sum[maxn]; 6 struct str 7 { 8 ll y,x; 9 }; 10 str que[maxn]; 11 int main() 12 { 13 int n,m; 14 dp[0]=sum[0]=0; 15 while(~scanf("%d%d",&n,&m)) 16 { 17 int head=0,tail=0; 18 que[0].x=que[0].y=0; 19 for(int i=1;i<=n;i++){scanf("%I64d",&sum[i]);sum[i]+=sum[i-1];} 20 for(int i=1;i<=n;i++) 21 { 22 while(tail>head) 23 { 24 str h1=que[head],h2=que[head+1]; 25 if(h2.y-h1.y<2*(h2.x-h1.x)*sum[i])head++; 26 else break; 27 } 28 dp[i]=que[head].y+sum[i]*sum[i]-2*que[head].x*sum[i]+m; 29 str ns;ns.x=sum[i];ns.y=dp[i]+sum[i]*sum[i]; 30 while(tail>head) 31 { 32 str h1=que[tail],h2=que[tail-1]; 33 if((ns.y-h1.y)*(h1.x-h2.x)<=(h1.y-h2.y)*(ns.x-h1.x))tail--; 34 else break; 35 } 36 que[++tail]=ns; 37 } 38 printf("%I64d\n",dp[n]); 39 } 40 return 0; 41 }
2.POJ 3709 K-Anonymous Sequence
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define read freopen("in.txt","r",stdin) 5 #define write freopen("out.txt","w",stdout) 6 using namespace std; 7 #define maxn 500005 8 typedef long long ll; 9 int num[maxn]; 10 ll sum[maxn],dp[maxn]; 11 struct str 12 { 13 int pos; 14 ll x,y; 15 } que[maxn]; 16 bool checkTail(int p1,int p2,ll tx,ll ty) 17 { 18 return (ty-que[p2].y)*(que[p2].x-que[p1].x) 19 <=(que[p2].y-que[p1].y)*(tx-que[p2].x); 20 } 21 bool checkHead(int p1,int p2,int pos) 22 { 23 return que[p2].y-que[p1].y<pos*(que[p2].x-que[p1].x); 24 } 25 int main() 26 { 27 //read;write; 28 int cas,n,m; 29 num[0]=sum[0]=0; 30 scanf("%d",&cas); 31 while(cas--) 32 { 33 scanf("%d%d",&n,&m); 34 for(int i=1;i<=n;i++) 35 { 36 scanf("%d",&num[i]); 37 sum[i]=num[i]+sum[i-1]; 38 } 39 for(int i=m;i<2*m;i++)dp[i]=sum[i]-i*num[1]; 40 int head=1,tail=0; 41 que[++tail].x=num[1],que[tail].y=0,que[tail].pos=0; 42 for(int i=2*m;i<=n;i++) 43 { 44 int t=i-m; 45 ll tx=num[t+1],ty=dp[t]-sum[t]+t*num[t+1]; 46 while(tail>head&&checkTail(tail-1,tail,tx,ty))tail--; 47 que[++tail].x=tx,que[tail].y=ty,que[tail].pos=t; 48 while(tail>head&&checkHead(head,head+1,i))head++; 49 int pos=que[head].pos; 50 dp[i]=dp[pos]+sum[i]-sum[pos]-(ll)(i-pos)*num[pos+1]; 51 } 52 printf("%I64d\n",dp[n]); 53 } 54 return 0; 55 }
3.UVALive 4726 Average
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 #define maxn 150000 6 char ss[maxn]; 7 long long sum[maxn]; 8 int que[maxn]; 9 bool checkTail(int p1,int p2,int p3) 10 { 11 return (sum[p3]-sum[p2])*(p2-p1)<=(sum[p2]-sum[p1])*(p3-p2); 12 } 13 bool checkHead(int p1,int p2,int p3) 14 { 15 return (sum[p3]-sum[p2])*(p3-p1)>=(sum[p3]-sum[p1])*(p3-p2); 16 } 17 int checkAns(int s1,int e1,int s2,int e2) 18 { 19 long long t1=(sum[e2]-sum[s2])*(e1-s1); 20 long long t2=(sum[e1]-sum[s1])*(e2-s2); 21 if(t1==t2)return 1; 22 else if(t1>t2)return 2; 23 return 0; 24 } 25 int main() 26 { 27 sum[0]=0; 28 int cas,n,m; 29 scanf("%d",&cas); 30 while(cas--) 31 { 32 scanf("%d%d",&n,&m); 33 scanf("%s",ss); 34 for(int i=1;i<=n;i++)sum[i]=sum[i-1]+ss[i-1]-'0'; 35 int head=1,tail=0,left=1,right=m; 36 for(int i=m;i<=n;i++) 37 { 38 int t=i-m; 39 while(tail>head&&checkTail(que[tail-1],que[tail],t))tail--; 40 que[++tail]=t; 41 while(tail>head&&checkHead(que[head],que[head+1],i))head++; 42 t=checkAns(left-1,right,que[head],i); 43 if(t==1&&right-left>i-que[head]-1)left=que[head]+1,right=i; 44 else if(t==2)left=que[head]+1,right=i; 45 } 46 cout<<left<<" "<<right<<endl; 47 } 48 return 0; 49 }
4[HNOI2008]玩具装箱toy
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /************************************************************** 2 Problem: 1010 3 User: sdau20104686 4 Language: C++ 5 Result: Accepted 6 Time:344 ms 7 Memory:2248 kb 8 ****************************************************************/ 9 10 #include <iostream> 11 #include <cstring> 12 #include <cstdio> 13 #include <cstdlib> 14 #include <cmath> 15 #include <string> 16 #include <vector> 17 #include <list> 18 #include <map> 19 #include <queue> 20 #include <stack> 21 #include <bitset> 22 #include <algorithm> 23 #include <numeric> 24 #include <functional> 25 using namespace std; 26 typedef long long ll; 27 #define read freopen("in.txt","r",stdin) 28 #define write freopen("out.txt","w",stdout) 29 #define maxn 50005 30 ll sum[maxn]; 31 ll dp[maxn]; 32 int que[maxn]; 33 ll n,m; 34 bool checkHead(int i,int j,int k) 35 { 36 ll tk=sum[k]+k,tj=sum[j]+j,ti=sum[i]+i; 37 ll rk=dp[k]+tk*tk+2*m*tk; 38 ll rj=dp[j]+tj*tj+2*m*tj; 39 if((rk-rj)<2*ti*(tk-tj))return true; 40 return false; 41 } 42 bool checkTail(int i,int j,int k) 43 { 44 ll tk=sum[k]+k,tj=sum[j]+j,ti=sum[i]+i; 45 ll rk=dp[k]+tk*tk+2*m*tk; 46 ll rj=dp[j]+tj*tj+2*m*tj; 47 ll ri=dp[i]+ti*ti+2*m*ti; 48 if((ri-rk)*(tk-tj)<=(rk-rj)*(ti-tk))return true; 49 return false; 50 } 51 int main() 52 { 53 //read; 54 while(~scanf("%I64d%I64d",&n,&m)) 55 { 56 m++; 57 sum[0]=0,dp[0]=0,que[0]=0; 58 for(int i=1;i<=n;i++) 59 { 60 scanf("%I64d",&sum[i]); 61 sum[i]+=sum[i-1]; 62 } 63 int head=0,tail=0; 64 for(int i=1;i<=n;i++) 65 { 66 while(tail>head&&checkHead(i,que[head],que[head+1]))head++; 67 ll t=(i-que[head]+sum[i]-sum[que[head]]-m); 68 dp[i]=dp[que[head]]+t*t; 69 while(tail>head&&checkTail(i,que[tail-1],que[tail]))tail--; 70 que[++tail]=i; 71 } 72 printf("%lld\n",dp[n]); 73 } 74 return 0; 75 }