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

单调栈与单调队列

单调栈与单调队列

你说我都会树链剖分了,咋还不会单调栈与单调队列啊?

单调栈

问题类型:对于一个长度为 nnn 的序列 aaa,对于 ∀1≤i≤n\forall 1\le i\le n∀1in,求 [a1∼ai−1][a_1\sim a_{i-1}][a1ai1][ai+1∼an][a_{i+1}\sim a_n][ai+1an] 的最靠近 iii 且比 aia_iai 大/小的问题。

以在 iii 左边的区间为例。

不过说最值问题可以用线段树、ST 表、树状数组之类的,为啥要学这个嘞?

  • 实现简单
  • 速度较快,毕竟线段树、树状数组常数都挺大的
  • 不会其他的:

那么我们来看看原理。

不妨先手搓一个样例玩玩。

假设数组为 [1,2,3,4,5][1,2,3,4,5][1,2,3,4,5],求最大值。

那么我们挨个看看 iii

iii数列
111[1][1][1]
222[1,2][1,2][1,2]
333[1,2,3][1,2,3][1,2,3]
444[1,2,3,4][1,2,3,4][1,2,3,4]
555[1,2,3,4,5][1,2,3,4,5][1,2,3,4,5]

发现第 iii 次操作有且仅有 aia_iai 被新加入数列。即 [a1∼ai−1][a_1\sim a_{i-1}][a1ai1] 是不变的。如果暴力枚举就会重复,效率极低。

所以我们考虑贪心的思想。

因为是求最大值,如果 ∃ i<j,ai≤aj\exist \ i<j,a_i\le a_j i<j,aiaj 的情况,是不是从现在开始,在 aia_iai 的有生之年就不可能对答案有贡献。

因为后面的区间如果包含了 aia_iai,也必定包含了 aja_jaj,所以 aia_iai 一定会被 aja_jaj 所替代,也就无法产生贡献。

那我们就不需要考虑 aia_iai,可以把它踢出去。

所以我们存储在序列里的元素一定是一个具有单调性的序列。

我们考虑用栈进行存储。

如果 i<j,ai≤aji<j,a_i\le a_ji<j,aiaj,即 aia_iai 已成为废品,那么就让它从栈中弹出。重复执行这一步骤。

最后再加入 aja_jaj 就算完成了一次操作。

来看看代码。

for(int i=1;i<=n;++i){while(!st.empty()&&h[st.top()]<h[i]){ans[st.top()]=i;st.pop();}st.push(i);}

虽然看起来是双层循环,是 O(n2)O(n^2)O(n2),但是如果仔细算算,发现 while 里面的操作总共最多执行 nnn,所以总时间复杂度是 O(n)O(n)O(n)

单调队列

单调栈的升级版。升级在于规定了区间长度为 mmm

即当前为 iii,与 iii 有关系的区间为 [i−m−1,i−1][i-m-1,i-1][im1,i1][i+1,i+m+1][i+1,i+m+1][i+1,i+m+1]。求最值。

以在 iii 左边的区间为例。

那么我们再来手搓玩玩。序列还是 [1,2,3,4,5][1,2,3,4,5][1,2,3,4,5],区间长度为 333

iii序列
111[1][1][1]
222[1,2][1,2][1,2]
333[1,2,3][1,2,3][1,2,3]
444[2,3,4][2,3,4][2,3,4]
555[3,4,5][3,4,5][3,4,5]

其中只有 i∈{3,4,5}i\in \left\{ 3,4,5\right\}i{3,4,5} 时才有意义。所以只需要考虑 3≤i≤53\le i\le 53i5

可能不够直观,换种方式看看?
[1 2 3] 4 5 1 [2 3 4] 5 1 2 [3 4 5] [1\ 2\ 3]\ 4\ 5\\ \ 1\ [2\ 3\ 4]\ 5\\ \ 1\ 2\ [3\ 4\ 5]\\ [1 2 3] 4 5 1 [2 3 4] 5 1 2 [3 4 5]
注意到,每次改动仅是多加上一个数 ai+1a_{i+1}ai+1,并减去一个数 ai−m+1a_{i-m+1}aim+1。所以说就是单调栈的升级版。

俗称滑动窗口。感觉十分形象。

这个是真正的区间最值。但上文说过,线段树、ST 表、树状数组都太慢且不容易实现。

老规矩,还是维护一个具有单调性双端队列,这样可以模拟从尾部进,从队头出。

对于每次“滑动”,先将滑动所加元素加入队列尾部,不断踢出不比其更优的元素。这个原理和单调栈相似。

然后由于是滑动,所以队列前面的元素可能已经超过了窗口,那我们将它踢出队列。

最后将新加元素加到队列尾部。

代码:

for(int i=1;i<=n;++i){while(!dq.empty()&&a[dq.back()]<=a[i])dq.pop_back();while(!dq.empty()&&dq.front()<=i-k)dq.pop_front();dq.push_back(i);ans[i]=a[dq.front()];}

完结撒花。

http://www.xdnf.cn/news/19908.html

相关文章:

  • ThinkPHP的log
  • C++二维数组的前缀和
  • 小杰机械视觉(finally)——题库上——gray、thresh、erode、dilate、HSV、开运算、ROI切割、矫正。
  • 博主必备神器~
  • HBase Region
  • 【代码解读】Deepseek_vl2中具体代码调用
  • 一款高效、强大的子域名爬取工具,帮助安全研究者和渗透测试人员快速收集目标域名的子域名信息
  • 服务器数据恢复—OceanStor存储数据丢失原来这样恢复
  • HOW - 在浏览器下载一个 Excel 表格文件
  • 基于SpringBoot的大学生就业招聘系统
  • 撤回通知(我自己的账号)
  • 自建局域网gitlab如何修改提交时间
  • 不做推销做共情:一个小众独立站靠宠物殡葬用品,年营收超3600万元
  • 机器学习笔记-第二周
  • 力扣:2458. 移除子树后的二叉树高度(dfs序)
  • 基于单片机车流车速检测系统设计
  • C++字符串操作:string类与数组对比
  • MySQL知识大全
  • ansible循环+判断(with,loop,when,if,for)
  • Python爬虫进阶:面向对象编程构建可维护的爬虫系统
  • Babylon 编辑器快捷键小记
  • 零构建的快感!dagger.js 与 React Hooks 实现对比,谁更优雅?
  • Python OpenCV图像处理与深度学习:Python OpenCV DNN模块深度学习与图像处理
  • 线程安全问题及解决方案
  • 163起融资,梅卡曼德融资额夺冠,钉钉、百度智能云10周年,汉桑科技IPO| 2025年8月人工智能投融资观察 · 极新月报
  • Android --- 搭建JNI框架
  • % g++ *.cpp ...: fatal error: ‘opencv2/opencv.hpp‘ file not found 1
  • 数论常见公式定理大全
  • 无需服务器,免费、快捷的一键部署前端 vue React代码--PinMe
  • 嵌入式学习 51单片机基础