传送门:点击打开链接
题意:有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; }