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

传送门难度
https://www.luogu.com.cn/problem/P1886普及/提高-

如题,单调队列板子题。
单调队列是一个双端队列,头和尾都可以出元素。
本文对单调队列的实现进行简述、模拟,方便理解,然后给出完整AC代码

符号说明

  • n:序列a的长度
  • k:窗口的长度
  • h:队首
  • t:队尾
  • a[]:存储序列a的数组
  • q[]:单调队列实现数组,存储的是序列a数组的下标index(index∈[0 , n-1]且index∈N)

分析

因为求最大值与求最小值的思路是基本相同的,所以以求最小值为例进行分析,即对void minqueue()进行分析。

  • 首先是队头和队尾
int h = 0, t = -1;//队首h,队尾t

这就是对队列进行模拟所采用的头尾。当队列为空时,总有h-t == 1成立。

  • 接着就是最核心的两个while循环和一步数组赋值q[++t] = i操作。
    • 第一个while主要是要保证单调队列的窗口长度不能大于所给的窗口长度
    • 第二个while主要是依据所给的减(增等条件)去除明显不可能的结果。
此处给出一个典型用例的模拟,看完就清楚了
典型用例模拟分析
6 3
1 3 -1 3 6 7

这个用例的特点是,index=2的元素-1比前面两个都小,而index=2,3,4,5,的这四个元素呈现递增。

典型用例模拟分析表格(minqueue()运行过程)
第id步ihtq
1(初始化状态)-0-1{}
200-1→0{0}
3100→1{0,1}
4201→0→-1→0{2}
5300→1{2,3}
6401→2{2,3,4}
750→12→3{3,4,5}

id = 4 那一行就是第二个while的效果
id = 7 那一行就是第一个while的效果

AC代码

#include<iostream>
#include<cstdio>

using namespace std;

int a[1000005];//序列
int n, k;//序列长度n,窗口长度k
int q[1000005];//模拟队列的数组

void minqueue() {//求最小值
	int h = 0, t = -1;//队首h,队尾t
	for (int i = 0; i < n; ++i) {
		while (h <= t && q[h] + k <= i)
			h++;
		while (h <= t && a[i] < a[q[t]])
			t--;
		q[++t] = i;
		if (i >= k - 1) {
			printf("%d", a[q[h]]);
			if (i != n - 1)
				printf(" ");
		}
	}
}


void maxqueue() {//求最大值
	int h = 0, t = -1;//队首h,队尾t
	for (int i = 0; i < n; ++i) {
		while (h <= t && q[h] + k <= i)
			h++;
		while (h <= t && a[i] > a[q[t]])
			t--;
		q[++t] = i;
		if (i >= k - 1) {
			printf("%d", a[q[h]]);
			if (i != n - 1)
				printf(" ");
		}	
	}
}


int main() {
	scanf("%d%d", &n, &k);
	for (int i = 0; i < n; ++i)
		scanf("%d", &a[i]);
	minqueue();
	printf("\n");
	maxqueue();
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值