单调队列总结
单调队列
就是保持队列中的元素始终保持单调性,这个数据结构就是单调队列
它的作用就是维护最值、求第一个比i小(大)的数的下标等等
还有个单调栈来着,不过我们可以用一个双端队列就足够了
如果要维护最大值,就用单调递减队列,反之,用递增队列
1、hdu3530 Subsequence 单调队列入门题
这题求的是最大值减去最小值不小于m不大于k的最长长度
这题用单调队列维护最大值和最小值即可
#include<iostream> using namespace std; #define MAX 1000005 int a[MAX],deq1[MAX],deq2[MAX]; int Min[MAX],Max[MAX]; int n,m,k; int main() { while(cin>>n>>m>>k){ for(int i=1;i<=n;i++) cin>>a[i]; int head1=0,tail1=0; int head2=0,tail2=0; int ans=0; int now=1; for(int i=1;i<=n;i++) { while(head1<tail1&&a[deq1[tail1-1]]<a[i]) tail1--; while(head2<tail2&&a[deq2[tail2-1]]>a[i]) tail2--; deq1[tail1++]=i; deq2[tail2++]=i; //cout<<head1<<head2<<endl; while(head1<tail1&&head2<tail2&&a[deq1[head1]]-a[deq2[head2]]>k) { if(deq1[head1]<deq2[head2])now=deq1[head1++]+1; else now=deq2[head2++]+1; } if(head1<tail1&&head2<tail2&&a[deq1[head1]]-a[deq2[head2]]>=m) { if(ans<i-now+1)ans=i-now+1; } } cout<<ans<<endl; } }
2、poj2559 Largest Rectangle in a Histogram
求包含的最大面积,维护两个单调队列,队列求第一个比i大(小)的数
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define MAX 1000005 int deq[MAX]; long long L[MAX],R[MAX]; long long h[MAX]; int n; int main() { while(~scanf("%d",&n)){ if(n==0) break; memset(h,-1,sizeof(h)); for(int i=1;i<=n;i++) scanf("%lld",&h[i]); memset(deq,0,sizeof(deq)); int head=0,tail=0; deq[tail++]=0; for(int i=1;i<=n;i++) { while(head<tail&&h[deq[tail-1]]>=h[i])tail--; L[i]=deq[tail-1]+1; deq[tail++]=i; } memset(deq,0,sizeof(deq)); deq[tail++]=n+1; for(int i=n;i>0;i--) { while(head<tail&&h[deq[tail-1]]>=h[i])tail--; R[i]=deq[tail-1]-1; deq[tail++]=i; } long long res=0; for(int i=1;i<=n;i++) { res=max(res,h[i]*(R[i]-L[i]+1)); } printf("%lld\n",res); } }
3、poj2823 Sliding Window
维护两个单调队列求最大和最小值即可
#include<iostream> #include<stdio.h> using namespace std; #define MAX 1000000 int n,k; int deq1[MAX],deq2[MAX]; int Max[MAX],Min[MAX]; int a[MAX]; int main() { while(~scanf("%d %d",&n,&k)) { int head1=0,tail1=0; int head2=0,tail2=0; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); while(head1<tail1&&a[deq1[tail1-1]]<a[i]) tail1--; while(head2<tail2&&a[deq2[tail2-1]]>a[i]) tail2--; deq1[tail1++]=i; deq2[tail2++]=i; while(head1<tail1&&i-deq1[head1]>=k) head1++; while(head2<tail2&&i-deq2[head2]>=k) head2++; Max[i]=a[deq1[head1]]; Min[i]=a[deq2[head2]]; } for(int i=k;i<=n;i++){ printf("%d ",Min[i]); } putchar('\n'); for(int i=k;i<=n;i++){ printf("%d ",Max[i]); } putchar('\n'); } }
4、poj3017 Cut the Sequence
题意是求把数列分成几块后每块的最大值的最小值,注意每块的和不超过M
题解:dp[i]表示分块后的最小值
则dp[i]=min{dp[i],dp[j]+max(a{j+1,i} }
这个dp可以用单调队列优化,维护一个单调递减队列,然后遍历队列里的值
#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> #include<math.h> using namespace std; typedef long long ll; #define MAX 100005 int n; ll m; ll a[MAX]; ll dp[MAX]; struct node { int index; ll val; }deq[MAX]; int main() { while(~scanf("%d %lld",&n,&m)){ bool flag=false; for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); if(a[i]>m) flag=true; } if(flag) { printf("-1\n"); continue; } else { int head=0,tail=0; int pos=1; ll sum=a[1]; deq[tail].val=dp[1]=a[1]; deq[tail++].index=1; for(int i=2;i<=n;i++) { sum+=a[i]; while(sum>m&&pos<i) sum-=a[pos],pos++; while(head<tail&&deq[tail-1].val<=a[i]) tail--; deq[tail].val=a[i];deq[tail++].index=i; while(head<tail&&deq[head].index<pos) head++; dp[i]=dp[pos-1]+deq[head].val; for(int j=head;j<tail-1;j++) dp[i]=min(dp[i],dp[deq[j].index]+deq[j+1].val); } printf("%lld\n",dp[n]); } } }