算法竞赛进阶指南---0x18(单调队列)滑动窗口

题面

算法竞赛进阶指南---0x18(单调队列)滑动窗口_第1张图片
算法竞赛进阶指南---0x18(单调队列)滑动窗口_第2张图片

题解

  1. 单调队列经典例题,考虑朴素做法,将窗口中的数放入队列,每次维护队列的数量,在O(k) 下找出窗口中的最小值/最大值 ,接下来对 O(k) 进行优化
  2. 对于窗口中的数,(第一个样例)当窗口移动的 1 3 -1 窗口中最小的数时 -1 ,接下来将 -3 加入到队尾,3 出队 现在窗口中的数就变为 3 -1 -3 ,我们观察对于新加入的数-3 是队列中的最小值,那么有了 -3 的存在,接下俩继续向右走的情况下,3 ,-1(比这个-3小的数肯定是用不到了),因为-3在3,-1的右边,在窗口中存在的时间比3,-1的长,在能取 -3 的 情况下绝对不会取 3,-1 ,那么就是说加入了-3,3,-1就是无用的数,我们就可以将其删除
  3. 那么我们就可以维护一个单调递增的队列,每次区间最小值就是队头的元素,以后每次向右滑动,遇到比队尾元素小的值,那么这个值就肯定相比于队尾元素是最优解,那么就将队尾元素删除,直到队列为空或者队尾元素小于当前值,最后将当前值加入到队尾
  4. 滑动窗口找区间最大值的思想和最小值一样,这里就不说明了,代码用数组模拟队列,具体看代码

代码

#include
#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;
const int N = 1e6 + 10;

int n, k;
int a[N], q[N];

int main() {

    cin >> n >> k;
    for (int i = 0; i < n; i++) scanf("%d", &a[i]);
    int hh = 0, tt = -1;
    for (int i = 0; i < n; i++) {
        //当队列中元素个数大于k时,队头出队
        while (hh <= tt && i + 1 - q[hh] > k) hh++;
        //当新加入的值小于队尾的值时,队尾删除
        while (hh <= tt && a[i] < a[q[tt]]) tt--;
        q[++tt] = i;
        if (i + 1 >= k)printf("%d ", a[q[hh]]);
    }

    cout << endl;

    hh = 0, tt = -1;
    for (int i = 0; i < n; i++) {
        //当队列中元素大于k时,队头出队
        while (hh <= tt && i + 1 - q[hh] > k) hh++;
        //当新加入的值大于队尾的值时,队尾删除
        while (hh <= tt && a[i] > a[q[tt]]) tt--;
        q[++tt] = i;
        if (i + 1 >= k)printf("%d ", a[q[hh]]);
    }

    return 0;
}


你可能感兴趣的:(#,队列,算法,数据结构,队列,单调队列)