先来看这个题,理解一下题意,我再来介绍单调队列
F. 滑动窗口
Description
上古文明遗迹中,有一个长度为k的滑动窗口从数组的左端滑到右端,试输出窗口每次移动时窗口里能看到的最大值和最小值。例如有数组为{1,3,-1,-3,5,3,6,7},窗口长度为3,则输出的最大最小值如表
Input
第一行为两个整数,即n和k(1<n≤1000000)。
第二行为n个整数。
Output
第一行为最小数,第二行为最大数(每行行末均有一空格)。
Samples
Input Copy
8 3
1 3 -1 -3 5 3 6 7
Output
-1 -3 -3 -3 3 3
3 3 5 5 6 7
首先要介绍一下单调队列
我们都知道队列是一种先进先出的数据结构
这就类似于我们在餐厅排队买饭,先排队的人总是先买到饭然后离开这个队列
那么单调队列就是符合队列先进先出特点并且队列元素是单调的数据结构
单调队列可以用数组和指针实现
我们定义一个数组 que[maxn] 来储存队列的下标,定义两个指针 head = 1,tail = 0 来表示头和尾
我们都知道队列有增加、删除这样的操作,对于单调队列来说,如果要增加一个元素,就是把这个元素放到队尾,那么我们直接给指针 tail++ ,然后把新的下标存储下来;如果要删除一个元素,就是让队首元素出队,那么就可以直接让指针 head–,向后指一位即可。
void add(){
tail++;
}
void erase(){
head--;
}
单调队列总是满足这样的性质
假设构造递增的单调队列
[i < j] a[i] < a[j]
[i < j] a[i] >= a[j] 不存在
所以我们就可以利用这样的性质去构造单调队列
当 head <= tail 并且 a[que[tail]] >= a[i] 的时候,如果把 a[i] 加入队列就不符合单调的性质,但是这个时候显然把单调队列的最后一个元素替换成现在的 a[i] 更优,所以我们更新当前的单调队列。递减的单调队列同理,把 >= 换成 <= 即可。
现在我们就可以做这道题了
int que[maxn], head = 1, tail = 0;
int n,k,a[maxn];
int main(){
io >> n >> k;
for(int i=1;i<=n;i++) io >> a[i];
for(int i=1;i<=n;i++){
while(head <= tail && a[que[tail]] >= a[i]) tail--;
que[++tail] = i;
while(i - que[head] + 1 > k) head++;
if(i >= k) printf("%d ",a[que[head]]);
}
puts("");
head = 1, tail = 0;
for(int i=1;i<=n;i++){
while(head <= tail && a[que[tail]] <= a[i]) tail--;
que[++tail] = i;
while(i - que[head] + 1 > k) head++;
if(i >= k) printf("%d ",a[que[head]]);
}
puts("");
return 0;
}