洛谷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;
}
### 关于洛谷平台上的队列相关C++编程练习题 在洛谷平台上,可以找到许多与队列相关的C++编程题目。这些题目通常涉及数据结构的应用以及算法的设计。以下是针对队列及其变种(如单调队列)的一些典型问题和解法。 #### 单调队列的基础概念 单调队列是一种特殊的队列结构,在某些场景下能够高效解决滑动窗口类的问题。其核心思想在于维护一个具有单调性质的队列,使得每次查询都能快速获取到目标值[^2]。 #### 实现单调队列的经典模板 下面展示的是基于C++实现的一个经典单调递减队列模板: ```cpp #include <bits/stdc++.h> using namespace std; int main(){ int n, k; cin >> n >> k; // 输入序列长度n 和 窗口大小k vector<int> a(n+1); deque<int> q; // 定义双端队列 for(int i=1;i<=n;i++) cin>>a[i]; // 读取数组元素 for(int i=1;i<=n;i++){ while(!q.empty() && q.front() <= i-k) q.pop_front(); // 移除超出窗口范围的元素 while(!q.empty() && a[q.back()] >= a[i]) q.pop_back(); // 维护单调性 q.push_back(i); // 插入当前索引 if(i>=k){ cout << a[q.front()] << " "; // 输出当前窗口内的最小值 } } } ``` 此代码片段展示了如何通过`deque`容器构建并操作单调队列,从而处理动态变化的数据集合中的最值问题。 #### 洛谷上推荐的相关练习题 对于希望深入学习队列应用的学生来说,可以通过尝试解答以下几道来自洛谷网站的经典习题来提升技能水平: - **P1886 滑动窗口**:这是一道典型的利用单调队列求解最大最小值的入门级题目。 - **P3950 跳房子**:虽然表面上看不是直接运用队列,但实际上也可以借助优先队列优化路径规划过程。 - **P2764 最大子矩阵 II**:涉及到二维空间下的区间查询,可能需要用到多层嵌套形式的队列管理策略。 每一道题都附带详细的样例说明及测试用例分析,便于理解具体需求后再着手编码实践[^3]。 #### 常见错误提示与调试技巧 当遇到运行时异常或者超时等问题时,可以从以下几个方面入手排查原因: - 数据规模是否匹配预期设定? - 是否存在不必要的重复计算环节? - 时间复杂度评估是否合理? 通过对上述几个维度逐一核查定位潜在漏洞所在位置,并采取相应措施加以修正即可有效提高提交成功率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值