P1886 滑动窗口 /【模板】单调队列

传送门

在这里插入图片描述

方法:单调队列

所用到的:
// hh 表示队首,tt表示队尾
int hh=0,tt=-1;
//用数组来模拟队列
int q[N],a[i];
思路:

其实简单来说,就是来维护队首元素。

就拿求最小值来时,当我们每一次将当前元素压入队列的时候,我们就要进行判断,如果队尾元素 > a[i] 的时候,我们就应该弹出当前队尾元素,对于数组而言 即 tt--,因为我们要保证队首为当前窗口的最小值。

当然,每一次压入元素的时候,都要判断一下,队首即最小值是不是为当前窗口所属的区间。

拿一个例子来说一下:

1 3 -1 -3 4 5 6四个元素,k=3

  • 首先将 1 的下标压入队列
  • 到第二个元素 3 时,我们要依次判断,队列里的元素是不是比3小,如果碰到比 3 还大的元素那么此元素元素一定不是最小值,将指向队尾的元素 tt --
  • 现在队列里的元素为 1 3
  • 到第三个元素 -1 时,进行依次判断,发现1 3 都比 -1 要大,那说明1和3 一定不是答案,将1 3 弹出队列,-1 变成的队首元素,输出 -1。
  • 到第四个元素 -3 时,和上面一样,发现 -1 比 -3 大,说明当前窗口区间的最小值一定不是 -1 ,将 -1 弹出 -3 压入,所以,当前区间最小值为 -3
    ……
    当到第七个元素时,我们发现,-3 已经不在当前 4 5 6这个区间,所以我们要把 -3 弹出;

这里我们不难发现,我们一直维护的都是队首元素,当我们求最大值也是一样。

Ac code:

#include <iostream>

using namespace std;

const int N = 1e6+10;

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

int main()
{
    scanf("%d%d",&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++)
    {
        //判断队首的 元素 是否属于当前的窗口的区间
        if(hh <= tt && i - k + 1 > q[hh])
            hh++;
        
        //目的是为了更新队首的最小值
        while(hh <=tt && a[q[tt]] >= a[i])
            tt--;
        
        q[++tt ]=i;
        
        //只有当 i >= k - 1的时候才能输出
        if(i>=k-1)
            printf("%d ",a[q[hh]]);
    }
    puts("");
    
    //求最大值
    hh=0;tt=-1;
    for(int i=0;i<n;i++)
    {
        if(hh <=tt && i-k+1 > q[hh])
            hh++;
        while(hh <= tt && a[q[tt]] <= a[i])
            tt--;
        q[++tt]=i;
        if(i>=k-1)
            printf("%d ",a[q[hh]]);
    }
    puts("");
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值