首页 > 其他分享 >单调队列

单调队列

时间:2024-02-05 13:46:06浏览次数:28  
标签:队列 最大值 qmax back int qmin 单调

单调队列是一种内部元素具有单调性的队列,可以解决求“区间内最值”的问题。

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

分析:暴力枚举的方式是进行 \(n-k\) 次循环,每次查找长度为 \(k\) 区间的最值,这样的算法时间复杂度是 \(O(nk)\) 的,无法通过这个题目。

以下分析以最大值为例,最小值同理。可以建立一个队列来维护这些数据,队首在左,队尾在右。首先队列为空,将元素加入到队列中。如果接下来的元素比队尾元素更大,那么将队尾元素出队,直到这个元素不大于队尾元素为止。当处理到的数据下标大于等于 \(k\),说明已经处理完了 \(k\) 个数字,可以查询这个区间的最大值了。这里的最大值就是队首元素。

在每次新加入元素之前,先要检查队首元素是否过期(不再处于需要统计的区间中):如果队首元素的下标小于等于 \(i-k\),说明队首元素过期了,此时队首元素就需要出队。

例如数据依次为[3,9,1,12,5,8,10,6],而k=4,维护过程如下:
image

这个队列不仅可以从队尾入队、队首出队,还可以从队尾出队(想象一个队伍在排队,排在最后的人发现后面要加入队伍的人惹不起,主动放弃了排队)。使用这种队列,保障队列内的元素具有单调性,这种队列被称为单调队列

#include <cstdio>
#include <deque>
using namespace std;
const int N = 1000005;
int a[N];
int main()
{
    int n, k; scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    deque<int> dq;
    for (int i = 1; i <= n; i++) {
        while (!dq.empty() && dq.front() <= i - k) dq.pop_front();
        while (!dq.empty() && a[dq.back()] >= a[i]) dq.pop_back();
        dq.push_back(i);
        if (i >= k) printf("%d%c", a[dq.front()], i == n ? '\n' : ' ');
    }
    dq.clear();
    for (int i = 1; i <= n; i++) {
        while (!dq.empty() && dq.front() <= i - k) dq.pop_front();
        while (!dq.empty() && a[dq.back()] <= a[i]) dq.pop_back();
        dq.push_back(i);
        if (i >= k) printf("%d%c", a[dq.front()], i == n ? '\n' : ' ');
    }
    return 0;
}

因为要知道队首元素是否过期,单调队列里面存储的实际上是下标而不是数据本身。

例:P2216 [HAOI2007] 理想的正方形

有一个 \(a \times b\) 的整数组成的矩阵,现请你从中找出一个 \(n \times n\) 的正方形区域,使得该区域所有数中的最大值和最小值的差最小。

保证 \(2 \le a,b \le 1000, n \le a, n \le b, n \le 100\),矩阵的元素均为不超过 \(10^9\) 的非负整数。

分析:暴力枚举的方式是枚举所有符合要求的小正方形,每次在正方形中查找最小最大值并统计,这样时间复杂度是 \(O(n^2 (a-n+1)(b-n+1))\),不能通过这个题目。

如何求出一个正方形矩阵的最大值呢?可以将这个正方形的每一行数字求最大值,然后再求每一行最大值中的最大值,剩下的一个值就是这个正方形矩阵的最大值。

image

以样例为例,首先处理每一行,找出一行中每一个长度为 \(2\) 的区间的最大值;然后处理每一列,找到一列中每一个长度为 \(2\) 的区间的最大值。最后得到的矩阵中的一个元素就对应着一个 \(2 \times 2\) 的正方形的最大值。使用相同的方式求得每个 \(2 \times 2\) 正方形的最小值,然后遍历这两个矩阵,计算这两个数的极差并统计即得到答案。

image

可以使用单调队列来求指定区间的最值。

#include <cstdio>
#include <deque>
#include <algorithm>
using namespace std;
const int N = 1005;
const int INF = 1000000000;
int mat[N][N], r1[N][N], r2[N][N], mn[N][N], mx[N][N];
int main()
{   
    int a, b, n;
    scanf("%d%d%d", &a, &b, &n);
    for (int i = 1; i <= a; i++)
        for (int j = 1; j <= b; j++)
            scanf("%d", &mat[i][j]);
    deque<int> qmin, qmax;
    for (int i = 1; i <= a; i++) {
        // 求每行区间最大、最小值
        qmin.clear(); qmax.clear();
        for (int j = 1; j < n; j++) {
            while (!qmin.empty() && mat[i][qmin.back()] >= mat[i][j]) 
                qmin.pop_back();
            qmin.push_back(j);
            while (!qmax.empty() && mat[i][qmax.back()] <= mat[i][j]) 
                qmax.pop_back();
            qmax.push_back(j);
        }
        for (int j = 1; j <= b - n + 1; j++) {
            while (!qmin.empty() && qmin.front() < j) 
                qmin.pop_front();
            while (!qmin.empty() && mat[i][qmin.back()] >= mat[i][j + n - 1]) 
                qmin.pop_back();
            qmin.push_back(j + n - 1);
            r1[i][j] = mat[i][qmin.front()];
            while (!qmax.empty() && qmax.front() < j) 
                qmax.pop_front();
            while (!qmax.empty() && mat[i][qmax.back()] <= mat[i][j + n - 1]) 
                qmax.pop_back();
            qmax.push_back(j + n - 1);
            r2[i][j] = mat[i][qmax.front()];
        }
    }
    for (int i = 1; i <= b - n + 1; i++) {
        // 在刚刚得到的新矩阵的基础上,求每列区间最大、最小值
        qmin.clear(); qmax.clear();
        for (int j = 1; j < n; j++) {
            while (!qmin.empty() && r1[qmin.back()][i] >= r1[j][i]) 
                qmin.pop_back();
            qmin.push_back(j);
            while (!qmax.empty() && r2[qmax.back()][i] <= r2[j][i])
                qmax.pop_back();
            qmax.push_back(j);
        }
        for (int j = 1; j <= a - n + 1; j++) {
            while (!qmin.empty() && qmin.front() < j) 
                qmin.pop_front();
            while (!qmin.empty() && r1[qmin.back()][i] >= r1[j + n - 1][i]) 
                qmin.pop_back();
            qmin.push_back(j + n - 1);
            mn[j][i] = r1[qmin.front()][i];
            while (!qmax.empty() && qmax.front() < j) 
                qmax.pop_front();
            while (!qmax.empty() && r2[qmax.back()][i] <= r2[j + n - 1][i]) 
                qmax.pop_back();
            qmax.push_back(j + n - 1);
            mx[j][i] = r2[qmax.front()][i];
        }
    }
    int ans = INF;
    for (int i = 1; i <= a - n + 1; i++)
        for (int j = 1; j <= b - n + 1; j++)
            ans = min(ans, mx[i][j] - mn[i][j]);
    printf("%d\n", ans);
    return 0;
}

标签:队列,最大值,qmax,back,int,qmin,单调
From: https://www.cnblogs.com/ronchen/p/18007794

相关文章

  • 苏维埃日报06.栈与队列的最简单实现(?)
    前言当年学数据结构的时候被栈和队列虐傻了当年真的没搞清这俩的进出顺序现在回过头来发现,退役了反而有点会了一个不恰当的比喻就像核糖体在mRNA上合成肽链一样,栈和队列的数据读入也是逐个读入但输出的时候,数据是带特定顺序输出的,如栈先进后出,队列先进先出,但是线性多肽水解......
  • Linux进程间通信_共享内存和消息队列
    本文对SystemV标准的共享内存和消息队列这两种进程间通信方式进行讨论,涉及原理、系统调用接口以及相关的内核数据结构,并给出相关示例代码。SystemV共享内存基本原理进程间通信必须要让不同的进程看到同一份内存资源,因为进程具有独立性,所以这份内存资源是操作系统提供的,接口是由......
  • 延迟队列-处理偶然性延迟任务的延迟队列
    目标:实现一个处理偶然事件的延迟队列。偶然事件发生的概率不高偶然事件一旦发生,事件的量多少不一期望偶然事件处理完成之后,能回收处理偶然事件的所有资源(因为偶然事件发生的概率低,所有分配的资源大部分时候都处于闲置状态) 思路:回收闲置资源发生偶然事件时,自动分配用于处......
  • JUC【1.原子类、2.锁Lock、3.阻塞队列、4.并发集合容器、5.并发工具类、6.线程池】、
    (JUC简介)转自极客时间1.JUC简介从JDK1.5起,JavaAPI中提供了java.util.concurrent(简称JUC)包,在此包中定义了并发编程中很常用的工具,比如:线程池、阻塞队列、同步器、原子类等等。JUC是JSR166标准规范的一个实现,JSR166以及JUC包的作者是同一个人DougLea。2.原......
  • Queue(队列)
    特性先进先出,允许再表的一端进行删除另一端进行插入运算。STL方式头文件#include<queue>定义queue<int>q;//建立一个队列q,其内部元素类型是int;函数q,push(a);//将元素a插入到队列q的末尾/q.pop();//删除队列q的队首元素。q.front();//查询q的队首元素。q.ba......
  • 函数 $f(x)$$=$$\log_x{(x+1)}$ 的单调性探究
    用两种方法对比较特殊的函数的单调性进行探究前言本博文就一个主题,探究函数\(f(x)\)\(=\)\(\log_x{(x+1)}\)由底数\(x>0\)且\(x\neq1\)且\(x+1>0\),可以得到该函数的定义域为\((0,1)\cup(1,+\infty)\),用电脑验证单调递减区间是\((0,1)\)和\((1,+\infty)\)......
  • (10/60)用栈实现队列、用队列实现栈
    用栈实现队列实现思路用两个栈实现。入队用输入栈stIn,出队用输出栈stOut。实现pop()时,要注意pop只删除,不返回值。复杂度分析略注意点stack的pop只能弹出,不返回值;弹出并获取值分成:用top()记录栈顶值、用pop()弹出(删除)栈顶值。class方法调用要用->。代码实现classMyQu......
  • 堆(优先队列)
    堆是一种树形结构,树的根是堆顶,堆顶始终保持为所有元素的最优值。有大根堆和小根堆,大根堆的根节点是最大值,小根堆的根节点是最小值。堆一般用二叉树实现,称为二叉堆。堆的存储方式堆的操作empty返回堆是否为空top直接返回根节点的值,时间复杂度\(O(1)\)push将新元素添加在......
  • kafka系列(一)【消息队列、Kafka的基本概念、Kafka的工作机制、Kafka可满足的需求、Kafk
    (kafka系列一)转自《Kafka并不难学!入门、进阶、商业实战》一、消息队列1.消息队列的来源在高并发的应用场景中,由于来不及同步处理请求,接收到的请求往往会发生阻塞。例如,大量的插入、更新请求同时到达数据库,这会导致行或表被锁住,最后会因为请求堆积过多而触发“连接数过多的......
  • Poj 3414 Pots (BFS+回溯+队列)
    这道题需要输出最后结果的执行过程,可以通过结构体,在结构体中定义一个数组s,s中存储了每一步的执行过程,实现了回溯。并且在运行中可以适当剪枝,减少枚举次数。 #include<iostream>#include<queue>#include<cstring>usingnamespacestd;constintN=110;intaa,bb,cc,vis[N......