题目描述

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

例如:

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

img

输入格式

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

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

输出格式

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

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

输入输出样例

输入 #1

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

输出 #1

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

说明/提示

50%的数据, $ n \leq 10^5 $

100%的数据,$ n \leq 10^6 $

前几天听肖大佬提到了单调队列,特来补题,不过似乎单调队列用的没有单调栈那么多:-)

思路:

维护两个单调的双端队列:

一个单调递增,该队列的最左端即为当前区间的最小元素。入队一个元素时,若队尾的元素>=当前元素,则它们对最小值毫无贡献,可直接弹出,直到队列为空或碰到比当前元素小的。然后队列左端也要把id不属于这个区间的弹出,之后更新答案

一个单调递减,该队列的最左端即为当前区间的最大元素。入队一个元素时,若队尾的元素<=当前元素,则它们对最大值毫无贡献,可直接弹出,直到队列为空或碰到比当前元素大的。然后队列左端也要把id不属于这个区间的弹出,之后更新答案。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e6 + 10;

int a[maxn];
int mina[maxn]; // [i, i + k - 1] 这个区间最小元素的下标
int maxa[maxn]; // [i, i + k - 1] 这个区间最大元素的下标

int main() {
int n, k;
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
deque<int> q1, q2;
for (int i = 0; i < n; i++) {
if (q1.empty()) { // 队列为空直接入队
q1.push_back(i);
} else {
while (!q1.empty() && a[q1.back()] >= a[i]) { // 当以前的值>=当前值时,
q1.pop_back(); // 则这些值就无用了,可直接弹出(求最小值)
}
q1.push_back(i); // 入队
while (q1.front() <= i - k) { // 把下标不在当前区间的队首元素弹出
q1.pop_front();
}
if (i >= k - 1) { // 获取答案
mina[i - k + 1] = q1.front();
}
}
if (q2.empty()) { // 队列为空直接入队
q2.push_back(i);
} else {
while (!q2.empty() && a[q2.back()] <= a[i]) { // 当以前的值<=当前值时,
q2.pop_back(); // 则这些值就无用了,可直接弹出(求最大值)
}
q2.push_back(i); // 入队
while (q2.front() <= i - k) { // // 把下标不在当前区间的队首元素弹出
q2.pop_front();
}
if (i >= k - 1) { // 获取答案
maxa[i - k + 1] = q2.front();
}
}
}
for (int i = 0; i < n - k + 1; i++) {
printf("%d ", a[mina[i]]);
}
printf("\n");
for (int i = 0; i < n - k + 1; i++) {
printf("%d ", a[maxa[i]]);
}
printf("\n");
return 0;
}