当前位置:   article > 正文

单调队列 + 例题:洛谷P2032 扫描 题解_洛谷日报 单调队列

洛谷日报 单调队列

因为博主本人水平有限,文章中难免会出现错误。您如果发现文章的错误敬请批评指正,感谢您的阅读^_^

一、什么是 单调队列

        单调队列 是一种具有单调性的结构,它可以用来求最大值最小值等操作,它是一种特殊的双端队列,它保持队列中元素的单调性单调队列可以在O(1)时间复杂度内求得队列中的最大值或最小值。在单调队列中,队列中的元素按照一定的顺序排列,并且队列中的元素满足某种单调性。常见的单调队列有单调递增队列单调递减队列。在单调递增队列中,队列中的元素按照递增的顺序排列;在单调递减队列中,队列中的元素按照递减的顺序排列。单调队列常用于求解滑动窗口问题,可以高效地获取滑动窗口内的最大值或最小值

二、单调队列的基本思想

        单调队列 的基本思想是:维护一个单调递增(或递减)的队列,队列中的元素按照从大到小(或从小到大)的顺序排列。每当窗口滑动时,如果新加入的元素大于队尾元素,则将队尾元素出队,直到队列为空或新加入的元素小于等于队尾元素为止。这样,队列的队首元素就是当前窗口的最大值。 

        这段话有点长,可以搭配后面的内容理解。

三、示意图 

以下是一个示意图,展示了单调队列的基本思想和操作过程:

初始状态:
队列:(5)

操作1:
入队元素3
此时3比5小,直接入队
队列:(5,3)

操作2:
入队元素2
此时2比3小,直接入队
队列:(5,3,2)

操作3:
入队元素4
此时进行判断,发现4比2大,2出队
再次进行判断,发现4比3大,3出队
再次进行判断,发现4比5小,4入队
队列:(5,4)

操作4:
入队元素2
队列:(5,4,2 )

操作5:
这里我们尝试出队一个元素
队列:(4,2)
我们会发现最大值依然是最左边的数
当前最大元素:4

        以上示意图展示了一个单调递减队列的操作过程,队列中始终保持着元素的递减顺序。在每次入队时,如果新元素比队尾元素大,则将队尾元素出队,直到队列为空或新元素比队尾元素小为止。这样就实现了维护一个单调递减序列的队列。

四、代码实现

下面给出单调队列在滑动窗口问题中的C++实现示例:

  1. #include <iostream>
  2. #include <deque>
  3. #include <vector>
  4. using namespace std;
  5. void maxSlidingWindow(int nums[], int k) {
  6. deque<int> dq;
  7. for (int i = 0; i < 8; i++) {
  8. // 如果队列不为空且队尾元素小于当前元素,将队尾元素出队
  9. while (!dq.empty() && nums[i] >= nums[dq.back()]) {
  10. dq.pop_back();
  11. }
  12. // 将当前元素入队
  13. dq.push_back(i);
  14. // 判断队首元素是否在滑动窗口内,如果不在则出队
  15. while (dq.front() <= i - k) {
  16. dq.pop_front();
  17. }
  18. // 判断滑动窗口是否形成,如果形成则将队首元素(最大值)加入结果列表
  19. if (i >= k - 1) {
  20. cout << nums[dq.front()] << " ";
  21. }
  22. }
  23. }
  24. int main() {
  25. int nums [] = {1, 3, -1, -3, 5, 3, 6, 7};
  26. int k = 3;
  27. maxSlidingWindow(nums, k);
  28. return 0;
  29. }
  1. 输出:
  2. 3 3 5 5 6 7


以上是一个完整的C++程序,实现了使用单调队列解决滑动窗口中的最大值问题。
示例中的输入数组 `nums` 为 {1, 3, -1, -3, 5, 3, 6, 7},窗口大小 `k` 为 3,程序的输出为 `3 3 5 5 6 7`,即滑动窗口中的最大值。

        在示例代码中,使用`deque`作为单调队列,通过维护队列的单调递减性质来求解最大值。程序首先遍历输入数组,将元素通过一定的逻辑加入到队列中,并保持队列的单调性。然后,判断队首元素是否在滑动窗口内,如果不在,则从队首弹出元素。最后,判断滑动窗口是否形成,如果形成则将队首元素(最大值)加入结果列表。

五、例题讲解

        洛谷P2032 扫描

   (一)题面

        1.题目描述

有一个 1×n 的矩阵,有 n 个整数。

现在给你一个可以盖住连续 k 个数的木板。

一开始木板盖住了矩阵的第 1∼k 个数,每次将木板向右移动一个单位,直到右端与第 n 个数重合。

每次移动前输出被覆盖住的数字中最大的数是多少。

        2.输入格式

第一行两个整数 n,k,表示共有 n 个数,木板可以盖住 k 个数。

第二行 n 个整数,表示矩阵中的元素。

        3.输入格式 

共 n−k+1 行,每行一个整数。

第 i 行表示第 i∼i+k−1 个数中最大值是多少。

   (二)题目解析 

         这道题我们可以创建一个单调队列,具体操作看一下代码和注释

   (三)具体代码

         老规矩,上代码,走你q(≧▽≦q)

  1. //今天你数组调大小了吗
  2. #include <bits/stdc++.h>
  3. #include <iostream>
  4. #include <algorithm>
  5. #include <queue>
  6. #include <cstring>
  7. #include <stack>
  8. #include <vector>
  9. #include <set>
  10. #include <cstdio>
  11. #include <cmath>
  12. #include <map>
  13. #include <cstdlib>
  14. using namespace std;
  15. struct point{//建立结构体
  16. int x; //存储元素
  17. int t; //元素的下标
  18. };
  19. int n,k;
  20. list<point> l; //这里我们用链表当单调队列
  21. int main(){
  22. cin>>n>>k;
  23. for(int i=1;i<=n;i++){
  24. int a;
  25. cin>>a; //临时存放元素
  26. while(!l.empty()&&l.back().x<=a){ //清除比它小的
  27. l.pop_back();
  28. }
  29. l.push_back({a,i}); //进入单调队列,此时在它左边没有比它小的
  30. //保证了队列的单调性
  31. if(l.front().t<i-k+1){
  32. l.pop_front(); //将木板盖不住的元素清除
  33. }
  34. if(i>=k){ //此时可以开始输出了
  35. cout<<l.front().x<<endl;
  36. }
  37. }
  38. return 0;
  39. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/寸_铁/article/detail/993866?site
推荐阅读
相关标签
  

闽ICP备14008679号