传送门:点击打开链接
题意:有n个数字,有个宽度为k的窗口,从最左边向右边移动,每次都框住k个数字,依次输出这些框中的最大值和最小值
思路:运用单调队列维护。一般deque我们都手动模拟,因为一般单调对列对时间复杂度要求都会比较高。
一般令cur=rear=0,rear表示尾指针,其实这个指针是取不到的,也就是说左开由闭。当cur<rear则表示队列中存在元素
单调队列数字都是从队尾进,要求最值的时候一般都是在堆首取。所以说,队首要求的是最大值还是最小值,也决定了整个队列是递增还是递减。
单调对列一般都是这样考虑问题的,假如我现在是要维护求最小值的单调队列
数字一般从队尾插入t,先要从队尾开始,将大于t的数字全部删除,然后才把t加入到队尾
这时,再考虑队首开始的是否都符合题意范围,把不符合的也删除,也就是cur++移动首指针。
然后再取队首元素,此时就是当前的最小值
#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<string>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#define fuck(x) cout<<"["<<x<<"]"
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w+",stdout)
using namespace std;
typedef long long LL;
const int MX = 1e6 + 5;
const int INF = 0x3f3f3f3f;
int A[MX];
int Q_1[MX], cur_1, tail_1;//Min
int Q_2[MX], cur_2, tail_2;//Max
int MIN[MX], MAX[MX];
int main() {
int n, k; //FIN;
while(~scanf("%d%d", &n, &k)) {
k = min(n, k);
cur_1 = tail_1 = cur_2 = tail_2 = 0;
for(int i = 1; i <= n; i++) {
A[i] = read();
}
for(int i = 1; i <= n; i++) {
while(cur_1 < tail_1 && A[Q_1[tail_1 - 1]] > A[i]) tail_1--; Q_1[tail_1++] = i;
while(cur_2 < tail_2 && A[Q_2[tail_2 - 1]] < A[i]) tail_2--; Q_2[tail_2++] = i;
if(i >= k) {
while(cur_1 < tail_1 && Q_1[cur_1] < i - k + 1) cur_1++;
while(cur_2 < tail_2 && Q_2[cur_2] < i - k + 1) cur_2++;
MIN[i - k + 1] = A[Q_1[cur_1]];
MAX[i - k + 1] = A[Q_2[cur_2]];
}
}
for(int i = 1; i <= n - k + 1; i++) {
printf("%d%c", MIN[i], i == n - k + 1 ? '\n' : ' ');
}
for(int i = 1; i <= n - k + 1; i++) {
printf("%d%c", MAX[i], i == n - k + 1 ? '\n' : ' ');
}
}
return 0;
}