赞
踩
传送门:P2032 扫描 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P2032
用暴力解法时间复杂度为O(k*n)显然是不能通过有的示例的,我们可以考虑优化,有什么算法可以直接得到k区间的最大值呢?
这个时候就引入了单调队列,我们知道单调队列可以维护一个区间的最大值和最小值,既然是单调队列,那我们是要用单调递增的队列,还是用单调递减的队列呢?
下面可以找到答案。
求解步骤和思路可以参考求m区间内的最小值或滑动窗口:
对于这题的本质就是滑动窗口求每个k区间的最大值,我们用一个单调队列去维护队首为最大值,也就是说这个单调队列为一个相对于队首来说的单调递减的队列,队首就是最大。要注意的是,每次入队都要保证队首在k区间内,还要保证队列的单调性。
单调递减队列有:(我这里的递减是相对于队首的递减)
- for(int i=1;i<=n;i++){
- while(q[front]<=i-k&&front<=rear)front++;//保证队首在k区间内
- while(front<=rear&&a[q[rear]]<=a[i])rear--;//保证队列的单调性
- q[++rear]=i; //入队
- }
front 和 rear 分别为头指针和尾指针。
如 案例中的 n=5,k=3 {1,5,3,4,2}
队列中情况 | 队列操作 |
1 | 队列为空,1入队 |
5 | 5>1,1出队,5入队 |
5 3 | 3<5,3入队 |
5 4 | 4>3,3出队,4<5,4入队 |
4 2 | 5超出了k区间,5出队,2<4,2入队 |
从队列的整个操作过程我们可以知道,队首就是我们要找的最大值,而且也维护了队首在k区间内,这就是单调队列求k区间的最大值的好处。
- #include<iostream>
- using namespace std;
- int q[2000005],a[2000005],n,k,front=1,rear=0;
- int main(){
- cin>>n>>k;
- for(int i=1;i<=n;i++){
- cin>>a[i];
- while(q[front]<=i-k&&front<=rear)front++;//保证队首在k区间内
- while(front<=rear&&a[q[rear]]<=a[i])rear--;//保证队列的单调性
- q[++rear]=i; //入队
- if(i>=k)cout<<a[q[front]]<<"\n";
- }
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。