单调队列题目练习

Posted saigyouji-yuyuko

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单调队列题目练习相关的知识,希望对你有一定的参考价值。

RT。由于本人dp很弱(或者说什么都弱),于是决定分模块刷题。单调队列就找了些题目(我水平已经沦落到了普及组qwq)练,顺便把以前做过的题都堆起来。以后做到的题再开新文章。


1.多重背包

不说了,很好推。放许久之前的幼稚代码

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int MAXN=1000+7;
 4 const int MAXV=10000+7;
 5 inline int Read(){
 6     int x=0,f=0;char ch;
 7     while(!isdigit(ch=getchar()))if(ch==-) f=1;
 8     while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^0),ch=getchar();
 9     return f?-x:x;
10 }
11 int dp[2][MAXV];
12 int q[MAXV];
13 int key[MAXV];
14 int T,n,W,f,r,val,w,k,ans,p;
15 
16 int main(){
17     T=Read();
18     while(T--){
19         W=Read(),n=Read();memset(dp,0,sizeof dp);p=0;
20         for(register int i=1;i<=n;++i,p^=1){
21             w=Read(),val=Read(),k=Read();
22             for(register int j=0;j<w;++j){
23                 q[f=r=1]=0;key[1]=dp[p][j]=dp[p^1][j];//重点在这个dp[i][j]=dp[i-1][j]我漏掉了,这个也是必须要延续的! 
24                 for(register int l=1;l*w+j<=W;++l){
25                     int x=dp[p^1][l*w+j]-l*val;
26                     while(f<=r&&key[r]<=x) --r;
27                     q[++r]=l;key[r]=x;
28                     while(f<=r&&q[f]<l-k) ++f;
29                     dp[p][l*w+j]=key[f]+l*val;
30                 }
31             }
32         }
33         printf("%d
",dp[n&1?0:1][W]);
34     }
35     return 0;
36 }
View Code

2.P3957 [NOIP2017T4]跳房子

套了一个二分答案的裸题,细节注意即可。

技术图片
 1 #include<bits/stdc++.h>
 2 #define mid ((L+R)>>1)
 3 using namespace std;
 4 typedef long long ll;
 5 const int N=500000+7;
 6 const ll INF=5e10;
 7 inline int read(){
 8     int x=0,f=0;char ch;
 9     while(!isdigit(ch=getchar()))if(ch==-) f=1;
10     while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^0),ch=getchar();
11     return f?-x:x;
12 }
13 ll dp[N];
14 int x[N],s[N],q[N];
15 int n,d,k,L=1,R=1e9,f,r;
16 
17 inline int check(int g){
18     int a=(d-g>0)?(d-g):1,b=d+g,y=1;register int i=1;
19     q[f=r=1]=0;
20     for(;x[i]<a;++i)dp[i]=-INF;
21     for(;i<=n;++i){
22         while(x[y]<=x[i]-a){//重点,调了好久. 
23             while(f<=r&&dp[y]>dp[q[r]])--r;
24             q[++r]=y++;
25         }//while(x[y]>=x[i]-b&&x[y]<=x[i]-a)这是本人原语句↑发现大数据过不了,主要是x[y]>=x[i]-b加上限制了前面dp的入队,导致y值一直就没办法增加了。 
26         while(f<=r&&x[q[f]]<x[i]-b)++f;
27         if(dp[q[f]]==-INF||f>r){f=r;dp[i]=-INF;continue;} else dp[i]=(dp[q[f]]+(ll)s[i]);
28         if(dp[i]>=k) return 1;
29     }
30     return 0;
31 }
32 
33 int main(){//freopen("tmp.in","r",stdin);
34     n=read(),d=read(),k=read();
35     for(register int i=1;i<=n;++i) x[i]=read(),s[i]=read();
36     while(L<R){
37         if(check(mid)) R=mid;
38         else L=mid+1;
39     }
40     printf("%d
",R==1e9?-1:R);
41     return 0;
42 }
View Code

3.poj2373 dividing the path

因为是很久以前写的代码,有不足之处但是现在也不想改了。注意区间标记的使用!

技术图片
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 using namespace std;
 6 const int L=1000000+7;
 7 const int INF=1000010;
 8 inline int read(){
 9     int x=0,f=0;char ch;
10     while(!isdigit(ch=getchar()))if(ch==-) f=1;
11     while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^0),ch=getchar();
12     return f?-x:x;
13 }
14 int dp[L],sum[L],q[L];
15 int n,l,x,y,a,b,f=1,r=1;
16 //细节稍微有点多,qwq 
17 int main(){
18     n=read(),l=read(),a=read(),b=read();
19     for(register int i=1;i<=n;++i) x=read(),y=read(),++sum[x+1],--sum[y];
20     for(register int i=1;i<=l;++i) sum[i]+=sum[i-1],dp[i]=INF;
21     for(register int i=(a<<1);i<(b<<1);i+=2){
22         if(!sum[i]) dp[i]=1;
23         while(f<=r&&dp[i-(a<<1)]<dp[q[r]])--r;
24         q[++r]=i-(a<<1);
25     }
26     for(register int i=(b<<1);i<=l;i+=2){
27         while(f<=r&&dp[i-(a<<1)]<dp[q[r]])--r;
28         q[++r]=i-(a<<1);
29         if(sum[i]>0)continue;
30         while(q[f]<i-(b<<1)) ++f;
31         dp[i]=min(dp[i],dp[q[f]]+1);
32     }
33     printf("%d
",dp[l]>=INF?-1:dp[l]);//for(int i=0;i<=l;++i) cerr<<i<<" "<<dp[i]<<" "<<sum[i]<<endl;
34     return 0;
35 }
View Code

4.UVA1169 Robotruck

很正常的一道水题。

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N=100000+7;
 5 inline int read(){
 6     int x=0,f=0;char ch;
 7     while(!isdigit(ch=getchar()))if(ch==-) f=1;
 8     while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^0),ch=getchar();
 9     return f?-x:x;
10 }
11 ll dp[N],dis[N],w[N],to[N],x,y,z,Lastx,Lasty;
12 int q[N];
13 int T,n,C,f,r;
14 
15 inline ll F(int x){return dp[x]+to[x+1]-dis[x+1];}
16 
17 int main(){
18     T=read();
19     while(T--){
20         C=read(),n=read();q[f=r=1]=0,Lastx=Lasty=0;
21         for(register int i=1;i<=n;++i){
22             x=read(),y=read(),z=read();
23             to[i]=x+y;dis[i]=dis[i-1]+abs(Lastx-x)+abs(Lasty-y);w[i]=w[i-1]+z;
24             Lastx=x;Lasty=y;
25         }
26         for(register int i=1;i<=n;++i){
27             while(f<=r&&w[i]-w[q[f]]>C)++f;
28             dp[i]=F(q[f])+dis[i]+to[i];
29             while(f<=r&&F(i)<F(q[r]))--r;
30             q[++r]=i;
31         }
32         printf("%lld
",dp[n]);
33         if(T) puts("");
34     }
35     return 0;
36 }
View Code

5.P2627 修剪草坪

大水题,枚举断点即可。边界问题的话右移一个格子就行了。

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;}
 5 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;}
 6 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
 7 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
 8 template<typename T>inline T read(T&x){
 9     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c==-)f=1;
10     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
11 }
12 const int N=100000+7;
13 ll f[N],a[N],sum[N];
14 int q[N];
15 int n,k,l,r;
16 inline ll F(int i){return f[i-1]-sum[i];}
17 //连单调队列也不会了。。 
18 int main(){//freopen("tmp.in","r",stdin);freopen("tmp.out","w",stdout);
19     read(n),read(k);
20     for(register int i=2;i<=n+1;++i)sum[i]=read(a[i])+sum[i-1];
21     q[l=r=1]=1;
22     for(register int i=2;i<=n+1;++i){
23         while(l<=r&&q[l]<i-k)++l;
24         f[i]=_max(f[i-1],F(q[l])+sum[i]);//cerr<<q[l]<<" "<<f[i]<<endl;
25         while(l<=r&&F(q[r])<=F(i))--r;
26         q[++r]=i;
27     }
28     printf("%lld
",_max(f[n],f[n+1]));
29     return 0;
30 }
View Code

6.P1901 发射站

一开始是为了练单调队列,结果往单调队列上想了半天没想出来,只会用单调栈,A了之后看了一眼题解,单调队列做法不就是把单调栈另一头加了一个head吗。。被算法标签误导。而且这题其实也不是dp。

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;}
 5 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;}
 6 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
 7 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
 8 template<typename T>inline T read(T&x){
 9     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c==-)f=1;
10     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
11 }
12 const int N=1000000+7;
13 int stk[N],r,n;
14 ll h[N],v[N],f[N],ans;
15 inline void dp(){
16     for(register int i=2;i<=n;++i){
17         while(r&&h[stk[r]]<=h[i])--r;
18         f[stk[r]]+=v[i];stk[++r]=i;
19     }
20 }
21 //连单调队列也不会了。。 
22 int main(){//freopen("tmp.in","r",stdin);freopen("tmp.out","w",stdout);
23     read(n);
24     for(register int i=1;i<=n;++i)read(h[i]),read(v[i]);
25     stk[r=1]=1,dp();
26     reverse(h+1,h+n+1),reverse(v+1,v+n+1),reverse(f+1,f+n+1);
27     stk[r=1]=1,dp();
28     for(register int i=1;i<=n;++i)MAX(ans,f[i]);
29     printf("%lld
",ans);
30     return 0;
31 }
View Code

 

以上是关于单调队列题目练习的主要内容,如果未能解决你的问题,请参考以下文章

单调队列优化dp题目

codevs3342绿色通道(单调队列优化dp)

单调栈

单调队列

POJ 2823 Sliding Window(单调队列)

牛客多校2021 K.King of Range(ST表+单调队列)