[ACM]初识斜率优化DP

刚刚接触斜率优化DP,做了几道比较基础的题,算是稍微有些理解了。

斜率优化DP的资料很多,最经典的资料还是周源04年OI论文《浅谈数形结合思想在信息学竞赛中的应用》,初学可以看它,进阶的话可以再去看看杨哲的《凸完全单调性的一个加强与应用》,做题可以搜题解。

几点学习心得:

1.动态规划时间复杂度:阶段*状态*决策所需时间。阶段*状态一般可视为状态总数,一般状态总数是确定的,我们无法减少它的遍历。那么,斜率优化DP就是在决策前通过维护策略的单调性减少状态数,降低一次决策所需的时间,从而降低时间复杂度。单调性如何维护由如何决策而定。

2.斜率优化DP一般需要先写出容易想到的状态转移方程,然后列举2个子状态发现是否对后续状态有单调性。

3.斜率优化DP一般通过单调队列维护状态,数形结合有助于解题,一般维护状态的单调性就是在2维坐标系中维护下凸折线。

4.需要参与决策的状态才能够入队,否则不要加入队列,或延迟加入它。特别注意新加入状态斜率与队尾最后2个状态斜率相等的情况,舍取要看题意。

列3道基础斜率优化DP题:

1.HDU 3507 Print Article

View Code
 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

View Code
 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

View Code
 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

View Code
 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 }

 

posted @ 2012-11-03 22:14  McFlurry  阅读(600)  评论(1编辑  收藏  举报