动态规划【10】之单调队列优化

单调队列例题

例题:luogu1886 滑动窗口 /【模板】单调队列

有一个长为 n n 的序列 a a ,以及一个大小为 k k 的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。
对于 100 % 100\% 的数据, 1 k n 1 0 6 , a i [ 2 31 , 2 31 ) . 1\leq k\leq n\leq 10^6, a_i\in[-2^{31},2^{31}).

直接思路

如果 n 1 0 5 n\leq 10^5 ,我们可以用线段树、ST表的方式解决。然而这题的 n n 达到了 1 0 6 10^6 ,开数组 O ( n log n ) O(n\log n) 就可能超过空间限制。

优化

我们将通过题目的例子 [ 1   3   1   3   5   3   6   7 ] [1\ 3\ -1\ -3\ 5\ 3\ 6\ 7] k = 3 k=3 来展示优化思路,这里我们求每次滑动后窗口中的最大值。

我们顺次加入数组中的数,
在第一窗口[1 3 -1]中,1在3之前,且1比3小,那么1在接下来的窗口中肯定不会成为最大值。因此,我们会直接删掉1,保留3和-1。保留-1的原因是-1在3的后面,有可能成为某个窗口的最大值。

3 -1 -3 5
顺次加入-3和5后,5比3,-1,-3都大, 3,-1,-3三个值都不可能成为最大值了,因此只保留5。(当然3也因为窗口大小 k = 3 k=3 被移除。)

重新整理思路

最大值

最大值:保留下来的数是递减的,这一过程可以用双端队列来维护。

1、维护队首(就是如果你已经是当前的m个之前那你就可以被删了,head++)

2、在队尾插入(每插入一个就要从队尾开始往前去除冗杂状态)1

最小值

最小值用和最大值类似的思路解决。

代码

/* ***********************************************
Author        : VFVrPQ
Created Time  : 日  8/11 06:52:43 2019
File Name     : luogu1886滑动窗口.cpp
Problem       : 
Description   : 
Solution      : 
Tag           : 
************************************************ */

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <iomanip>
using namespace std;
#define DEBUG(x) cout<<x<<endl;
const int N = 1e6+10;
const int M = 1e9+7;
const int INF = 1e9+7;

int a[N];
int n,k;
int minn[N],maxx[N], id[N];
int main()
{
    scanf("%d%d",&n,&k);
    for (int i=1;i<=n;i++)scanf("%d",&a[i]);
    int head=0, tail=0;
    for (int i=1;i<=n;i++){
        if (head<tail && id[head]<i-k+1) head++; //队首移动
        while (head<tail && minn[tail-1]>=a[i]){//升序的队列
            tail--;
        }
        minn[tail] = a[i];
        id[tail]=i;
        tail++;
        if (i>=k) printf("%d ",minn[head]);
    }
    printf("\n");
    head=tail=0;
    for (int i=1;i<=n;i++){
        if (head<tail && id[head]<i-k+1) head++; //队首移动
        while (head<tail && maxx[tail-1]<=a[i]){//降序的队列
            tail--;
        }
        maxx[tail] = a[i];
        id[tail]=i;
        tail++;
        if (i>=k) printf("%d ",maxx[head]);
    }
    printf("\n");
    return 0;
}

  1. https://www.luogu.com.cn/problemnew/solution/P1886 ↩︎

发布了74 篇原创文章 · 获赞 30 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/MustImproved/article/details/104773320