P1886 滑动窗口(单调队列)

题目描述

现在有一堆数字共N个数字(N<= 1 0 6 10^6 106),以及一个大小为k的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。

例如:

The array is [1 3 -1 -3 5 3 6 7], and k = 3.

img

输入格式

输入一共有两行,第一行为n,k。

第二行为n个数(<INT_MAX).

输出格式

输出共两行,第一行为每次窗口滑动的最小值

第二行为每次窗口滑动的最大值

输入输出样例

输入 #1

8 3
1 3 -1 -3 5 3 6 7

输出 #1

-1 -3 -3 -3 3 3
3 3 5 5 6 7

说明/提示

50%的数据, n < = 1 0 5 n<=10^5 n<=105

100%的数据, n < = 1 0 6 n<=10^6 n<=106

题解

  • 单调队列模板题

  • 单调队列顾名思义它是单调的(雾 因此一个队列要想是单调队列就必须要满足单调性(废话

  • 那么对于本题来说我们该如何维护它的单调性呢?

  • 以最小值为例

  • 首先假设原数组有两个相邻元素 x , y x, y x,y,如果 x > y x>y x>y a [ x ] > a [ y ] a[x]>a[y] a[x]>a[y] ,那么 x x x无疑没有 y y y优秀。以为 x x x的值更大且更靠前,那么在窗口的滑动过程中, x x x一定先比 y y y出队

  • 根据上面的性质我们就可以推出单调队列的更新方式。 即当循环到 i i i时,判断队尾与 a [ i ] a[i] a[i]的大小关系。如果 a [ t a i l ] > = a [ i ] a[tail]>=a[i] a[tail]>=a[i]则将 t a i l tail tail出队,知道不能再更新为止。 然后判断对头在原数组中的位置与 i i i是否相差 k k k,将对头出队。当队列合法时,对头即为所求答案

  • 最大值同理

code

#include <bits/stdc++.h> 
using namespace std; 
const int maxn = 1e6 + 100; 

template <class T> 
inline void read(T &s) {
	s = 0; T w = 1, ch = getchar(); 
	while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
	while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
	s *= w; 
}

int n, k; 
int a[maxn], q[maxn], p[maxn]; 

void get_max() {
	int head = 1, tail = 0; 
	for (int i = 1; i <= n; ++i) {
		while (head <= tail && q[tail] <= a[i]) tail--; 
		q[++tail] = a[i]; p[tail] = i; 
		while (p[head] <= i - k) ++head; 
		if (i >= k) printf("%d ", q[head]); 
	}
}

void get_min() {
	int head = 1, tail = 0; 
	for (int i = 1; i <= n; ++i) {
		while (head <= tail && q[tail] >= a[i]) tail--; 
		q[++tail] = a[i]; p[tail] = i; 
		while (p[head] <= i - k) ++head; 
		if (i >= k) printf("%d ", q[head]); 
	}
	puts(""); 
}

int main() {
	read(n), read(k); 
	for (int i = 1; i <= n; ++i) read(a[i]); 
	
	get_min(); 
	get_max(); 
	return 0; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值