题意分析:
包含n个数字的数列,用一个长度为k的窗口移动从1开始向右移动,每次输出窗口中的最小值和最大值。分两行输出结果:最小值输出到上行,最大值输出到下行。
解题思路:
考虑每次移动窗口要进行的操作,总共有两种,增加一个数,删去一个数。考虑到需要涉及到删除操作,我们使用单调队列来进行维护。
L代表队列的左端,R代表队列的右端,以递增队列为例:
如果我们在队列中存储当前的值,那么整个删除操作会特别耗时。转换思路,考虑在队列中存放当前值的下标,而依靠值来排序。
那么队列左端第一个存储的下标,就是最小的值对应的下标。每次增加值从右端往左插入,如果队列中的那个值大于当前值,就可以直接丢弃(因为这个值出现的时间早于当前值,之后肯定不会成为最小值)。输出最小值的话,我们从左端往右端,第一个满足i - 下标 < k的值,就是我们要的满足条件的最小值。
个人感受:
存下标真是太机智了。
具体代码如下:
#include<stdio.h> int a[1000100], q[1000100]; int main() { int n, k; while (~scanf("%d%d", &n, &k)) { int l = 0, r = 0, i, j; for (i = 0; i < n; ++i) scanf("%d", &a[i]); q[r] = 0; for (i = 1; i < k; ++i) { while (r >= 0 && a[q[r]] >= a[i]) --r; q[++r] = i; } printf("%d", a[q[l]]); for (i = k; i < n; ++i) { while (r >= l && a[q[r]] >= a[i]) --r; q[++r] = i; while (i - q[l] >= k) ++l; printf(" %d", a[q[l]]); } putchar('\n'); l = r = 0; q[r] = 0; for (i = 1; i < k; ++i) { while (r >= 0 && a[q[r]] <= a[i]) --r; q[++r] = i; } printf("%d", a[q[l]]); for (i = k; i < n; ++i) { while (r >= l && a[q[r]] <= a[i]) --r; q[++r] = i; while (i - q[l] >= k) ++l; printf(" %d", a[q[l]]); } putchar('\n'); } return 0; }