当前位置: 首页 > news >正文

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

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

题目描述

有一个长为 nnn 的序列 aaa,以及一个大小为 kkk 的窗口。现在这个窗口从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最小值和最大值。

例如,对于序列 [1,3,−1,−3,5,3,6,7][1,3,-1,-3,5,3,6,7][1,3,1,3,5,3,6,7] 以及 k=3k = 3k=3,有如下过程:

窗口位置最小值最大值[1   3  -1] -3   5   3   6   7 −13 1  [3  -1  -3]  5   3   6   7 −33 1   3 [-1  -3   5]  3   6   7 −35 1   3  -1 [-3   5   3]  6   7 −35 1   3  -1  -3  [5   3   6]  7 36 1   3  -1  -3   5  [3   6   7]37\def\arraystretch{1.2} \begin{array}{|c|c|c|}\hline \textsf{窗口位置} & \textsf{最小值} & \textsf{最大值} \\ \hline \verb![1 3 -1] -3 5 3 6 7 ! & -1 & 3 \\ \hline \verb! 1 [3 -1 -3] 5 3 6 7 ! & -3 & 3 \\ \hline \verb! 1 3 [-1 -3 5] 3 6 7 ! & -3 & 5 \\ \hline \verb! 1 3 -1 [-3 5 3] 6 7 ! & -3 & 5 \\ \hline \verb! 1 3 -1 -3 [5 3 6] 7 ! & 3 & 6 \\ \hline \verb! 1 3 -1 -3 5 [3 6 7]! & 3 & 7 \\ \hline \end{array} 窗口位置[1   3  -1] -3   5   3   6   7  1  [3  -1  -3]  5   3   6   7  1   3 [-1  -3   5]  3   6   7  1   3  -1 [-3   5   3]  6   7  1   3  -1  -3  [5   3   6]  7  1   3  -1  -3   5  [3   6   7]最小值133333最大值335567

输入格式

输入一共有两行,第一行有两个正整数 n,kn,kn,k
第二行有 nnn 个整数,表示序列 aaa

输出格式

输出共两行,第一行为每次窗口滑动的最小值;
第二行为每次窗口滑动的最大值。

输入输出样例 #1

输入 #1

8 3
1 3 -1 -3 5 3 6 7

输出 #1

-1 -3 -3 -3 3 3
3 3 5 5 6 7

说明/提示

【数据范围】
对于 50%50\%50% 的数据,1≤n≤1051 \le n \le 10^51n105
对于 100%100\%100% 的数据,1≤k≤n≤1061\le k \le n \le 10^61kn106ai∈[−231,231)a_i \in [-2^{31},2^{31})ai[231,231)

解析:

无法暴力T_T

暴力枚举的方式是进行 n−kn-knk 次循环,每次查找长度为k的区间的最值。这样的算法的时间复杂度为 O(n2)O(n^2)O(n2) ,无法通过本题。

单调队列

可以建立一个队列来维护这些数据。这个队列有一些特点就是:队列中的数都是原序列 aaa 中数字 aia_iai 的下标 iii ,队列随着窗口滑动而更新,保证队列中存的下标都处于当前窗口中。并且保持队首为窗口中最大数的下标。

如何实现

那么如何实现呢?假设我们找每个区间的最小值:将原序列中每个数 aia_iai 作为一个元素依次进行如下操作:如果队列为空,直接将当前元素下标 iii 入队。当队列不为空时,在加入新元素之前,检查队尾数 fff 代表的原序列数 afa_faf 是否小于 aia_iai ,若小于,则将队尾数删除,多次判断,直到队尾数 fff 代表的原序列数 afa_faf 大于 aia_iai 或队列为空。然后将 iii 放入队尾。再检查队首数 sss ,若 sss 已在我们的区间外,则将队首删去。则每段区间的最大值就是操作完的队列的队首数 sss 表示的原序列数 asa_sas

正确性验证

那么为何这样实现是正确的呢?因为我们的操作都保证了队列中的数 xxx 表示的 axa_xax 一定是单调递减的,且 asa_sas 一定是最大的,当 asa_sas 不在序列后,队列中第二个数 ttt ,一定满足 t>st>st>s。虽然 tttsss 没有必然的数量关系,但是 a(s+1)→(t−1)<at<asa_{(s+1)→(t-1)}<a_t<a_sa(s+1)(t1)<at<as,也就是说只要 ata_tat 还在序列,ata_tat 一定就是最大值!

几个注意点

但是注意最小值并不是每次操作之后队尾 fff 表示的数 afa_faf ,因为在之前的操作中可能存在最小值在队尾,而新判断的元素 ai>afa_i>a_fai>af 导致 afa_faf 被出队,iii 入队,而 aia_iai 并不是区间最小值。所以与求最大值相似,求最小值要条件相反地建一个单调队列。

由于开始时 i<ki<ki<k ,队列的变化无法表达整个区间,等到 i≥ki \geq kik 了,才可以表示第 i−k+1i-k+1ik+1 个区间的最值。

代码实现

由于两端都有出队操作,STLSTLSTL 中的普通队列 queuequeuequeue 已经无法满足。所以用 STLSTLSTL 中另一个神奇的双端队列 dequedequedeque

deque相关

deque介绍:

首尾都可插入和删除的队列为双端队列。

deque声明:

deque<元素类型> 名称; 例: deque dq;

deque在本题中可能用到的函数:

  • 队列名.push_back(x); 把x插入队尾后
  • 队列名.push_front(x); 把x插到队首
  • 队列名.back(); 返回队尾元素
  • 队列名.front(); 返回队首元素
  • 队列名.pop_back(); 删除队尾元素
  • 队列名.pop_front(); 删除队首元素
  • 队列名.empty(); 判断双端队列是否空
  • 队列名.clear(); 清空双端队列

CoDe

代码与解析基本相符,注释略。

#include<bits/stdc++.h>
using namespace std;
int n,k;
int a[1000005];
int mn[1000005];
int mx[1000005];
deque<int>dq;
int main(){cin>>n>>k;for(int i=1;i<=n;i++){cin>>a[i];}for(int i=1;i<=n;i++){while(!dq.empty()&&i-dq.front()+1>k) dq.pop_front();while(!dq.empty()&&a[dq.back()]>a[i]) dq.pop_back();dq.push_back(i);if(i>=k){mn[i]=a[dq.front()];}}for(int i=k;i<=n;i++){cout<<mn[i]<<' ';}cout<<endl;dq.clear();for(int i=1;i<=n;i++){while(!dq.empty()&&i-dq.front()+1>k) dq.pop_front();while(!dq.empty()&&a[dq.back()]<a[i]) dq.pop_back();dq.push_back(i);if(i>=k){mx[i]=a[dq.front()];}}for(int i=k;i<=n;i++){cout<<mx[i]<<' ';}return 0;
}

当然你用自己手写的双向队列来实现操作也可以,代码就不放了。我看其他大部分人都是用手写双向队列来实现操作的,代码很好找。

单点队列一般不作为单独题目出现,而是作为动态规划的一种优化出现在一些较难的动态规划题中

求赞QAQ

http://www.dtcms.com/a/329429.html

相关文章:

  • 《Foundations and Recent Trends in Multimodal Mobile Agents: A Survey》论文精读笔记
  • [量化交易](1获取加密货币的交易数据)
  • 面试实战 问题三十 HTTP协议中TCP三次握手与四次挥手详解
  • 解决程序连不上RabbitMQ:Attempting to connect to/access to vhost虚拟主机挂了的排错与恢复
  • 循序渐进学 Spring (上):从 IoC/DI 核心原理到 XML 配置实战
  • DataOceanAI Dolphin(ffmpeg音频转化教程) 多语言(中国方言)语音识别系统部署与应用指南
  • Vue 3 源码解读与核心 API 分析
  • EN 62368消费电子、信息技术设备和办公设备安全要求标准
  • mybtias集成spring原理?--spring,mybatis源码解析
  • 后端Web实战-MySQL数据库
  • Si an(1)
  • Linux高级编程-framebuffer
  • 华为悦盒EC6108V9-1+4G版-盒子有【蓝色USB接口】的特殊刷机说明
  • 数据分析全景:从数据到决策的完整链路与核心要义
  • 《Python学习之基础语法2:掌握程序流程控制的艺术》
  • 【分布式 ID】一文详解美团 Leaf
  • TCP Socket 编程实战:实现简易英译汉服务
  • 函数扇入数(Fan-in)
  • NAT技术、代理服务器+网络通信各层协议
  • transforms的使用 小土堆pytorch记录
  • 深度学习流体力学:基于PyTorch的物理信息神经网络(PINN)完整实现
  • PyTorch Tensor完全指南:深度学习数据操作的核心艺术
  • C++编程学习(第21天)
  • LeetCode 分类刷题:1004. 最大连续1的个数 III
  • 【Linux】常用命令(三)
  • 智慧养老丨实用科普+避坑指南:科技如何让晚年生活更安全舒适?
  • 编译 C++ 程序时提示:fatal error: ‘json/json.h‘ file not found
  • 使用 HTML5 Canvas 打造炫酷的数字时钟动画
  • Linux基本操作命令
  • Go 语言函数详解:从基础到高阶的行为逻辑构建