单调队列,单调栈相关

说起这个话题,应该很多人会有一种似有所悟,但又不敢确定的感觉。

(我差不多就是那样)

没错,这正是因为其中“单调”一词的存在。

  • 那么单调是什么?
  • 学过函数的人都知道单调函数或者函数的单调性吧
  • 其实直白一点说单调,就是一直增或一直减。
  •     eg:1,3,5,9就是一个单调增数列,数列中不存在后一个数比前一个数小的现象。


那么同样,在这里谈到的话题也有类似特点。

(一)单调队列

  其实就是一个符合单调性质的队列,但它同时具有单调的性质以及队列的性质。

使用频率不算高,但却占有至关重要的地位。它的作用很简单,就是为了维护一组单调数据,让我们在运行的过程中能够快速寻求前k个或后k个中最大或最小的值。

  (二)单调栈

  就是一个符合单调性质的栈并且它具有单调的性质以及栈的性质。



上面的两个在作用方面是相同的,差别仅是在编程过程中维护的数组的方式不同

(单调队列有两个指针,分别代表头和尾;单调栈有一个指针,代表头指针)

下面举个简单的栗子来解释单调队列及单调栈。

eg:有一组数据:1,5,9,4,7,8,6,将他们依此输入。同时,在某一时刻会让你求出后n个数中的最大值。

根据题意,我们可以得出这样一个结论:

    若后一个数大于前一个数,则结果必定不会是前一个数

    (比如现在输入了1,5,由于1<5,所以无论是后几个数中的最大值均不会为1)。

    因此,我们只需维护一个单调递减的数组便可快速求得所需值。

其中数组变化如下:

输入——1,数组——1;
输入——5,由于5>1删去1添入5,数组——5;
输入——9,由于9>5删去5添入9,数组——9;
输入——4,由于4<9直接添入,数组——9,4;
输入——7,由于7>4同时7<9因此删去4添入7,数组——9,7;
输入——8,由于8>4同时8<9因此删去7添入8,数组——9,8;
输入——6,由于6<8直接添入,数组——9,8,6。

总的来说,它的本质就是当你在插入一个值时,应将在他之前存入的所有小于他的数值剔除,再将他存入数组中。

基础就差不多就这样ok辣~


良心推荐一些题:

单调队列专题:

1.P1440 求m区间内的最小值 直通

思路:

  就是进行求解固定区间之内的最小值

上代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 1e6 + 5;
int n,k,l,r;
int a[N],q[N],p[N];

int main() {
    scanf("%d%d",&n,&k);
    for(int i=1; i<=n; i++) scanf("%d",&a[i]);
    for(int i=1; i<=n; i++) {
        if(q[l]<=i-k) l++;
        while(l<=r && a[i]<=a[q[r]]) r--; //单调递增
        q[++r]=i;
        p[i]=a[q[l]];
    }
    for(int i=k; i<=n; i++) printf("%d ",p[i]);
    printf("\n");
    memset(p,0,sizeof(p));
    l=0,r=0;
    for(int i=1; i<=n; i++) {
        if(q[l]<=i-k) l++;
        while(l<=r && a[i]>=a[q[r]]) r--; //单调递减
        q[++r]=i;
        p[i]=a[q[l]];
    }
    for(int i=k; i<=n; i++) printf("%d ",p[i]);
    return 0;
}


2.luogu P1886 滑动窗口 直通

思路:

  单调队列的裸题,练手题,必刷!

  而且不仅有求最大值还有求最小值哦!

坑点:

  看清楚输出的是什么!

上代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 1e6 + 5;
int n,k,l,r;
int a[N],q[N],p[N];

int main() {
    scanf("%d%d",&n,&k);
    for(int i=1; i<=n; i++) scanf("%d",&a[i]);
    for(int i=1; i<=n; i++) {
        if(q[l]<=i-k) l++;
        while(l<=r && a[i]<=a[q[r]]) r--; //单调递增
        q[++r]=i;
        p[i]=a[q[l]];
    }
    for(int i=k; i<=n; i++) printf("%d ",p[i]);
    printf("\n");
    memset(p,0,sizeof(p));
    l=0,r=0;
    for(int i=1; i<=n; i++) {
        if(q[l]<=i-k) l++;
        while(l<=r && a[i]>=a[q[r]]) r--; //单调递减
        q[++r]=i;
        p[i]=a[q[l]];
    }
    for(int i=k; i<=n; i++) printf("%d ",p[i]);
    return 0;
}


3.luogu P1714 切蛋糕 直通

思路:

  因为求的是幸运总和最大

  总和?

  当然就是求一下前缀和辣~

  所以我们首先求一个前缀和,然后维护一个单调递增的前缀和,最后更新答案时则使用max(ans,sum[i]-sum[q[l]])就结束辣!

坑点:

  注意这里ans的初始化是什么,我认为应该初始化为-99999999什么的,不过初始化为0也是可以的!

  因为拥有ans=max(ans,sum[i]-sum[q[l]])这一步,所以如果当前的i==q[l]那么sum[i]-sum[q[l]]一定为0,所以无论你初始化ans多么小,ans最小都只能是0,所以需要把ans输出化为0.

上代码:

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

const int N = 500005;
int n,m,ans,l,r;
int sum[N],q[N];

int main() {
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++) {
        scanf("%d",&sum[i]);
        sum[i]+=sum[i-1];
        if(q[l]<i-m) l++;
        while(l<=r && sum[q[r]]>sum[i]) r--; //维护一个单调增区间
        q[++r]=i;
        ans=max(ans,sum[i]-sum[q[l]]);
    }
    printf("%d",ans);
    return 0;
}


4.P1638 逛画展 直通

思路:

  我们需要用桶来维护一下单调队列,记录一下每个画家的作品出现的次数,如果队头的元素跟后面的元素有重复,那么就删除掉这个元素,以此类推

  然后最后的更新的话,必须保证所有颜色均出现过了,还需要记录一个last,表示上一次更新的区间的长度是多少。

  如果现在的区间长度比last要小,并且所有颜色均出现,那么就更新a,b端点坐标以及last,然后继续重复即可

上代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 1000001;
const int M = 2001;
int n,m,last=1<<30,a,b;
int x[N],c[M];

int main() {
    scanf("%d%d",&n,&m);
    for(int r=1,l=1,now; r<=n; r++) {
        scanf("%d",&x[r]);
        if(c[x[r]]==0) m--;
        c[x[r]]++;
        while(l<r && c[x[l]]>1) {
            c[x[l]]--;
            l++;
        }
        now=r-l+1;
        if(m==0 && now<last) last=now,a=l,b=r;
    }
    printf("%d %d",a,b);
    return 0;
}

额...至于单调栈什么的,我大概就做过一个题qwq,就光贴上地址吧

直通

ok,先暂时说这么多,以后我如果做到类似的题目还会发啦~

时间: 2024-10-09 09:08:58

单调队列,单调栈相关的相关文章

单调队列单调栈

单调队列单调栈 Tags:数据结构 更好阅读体验:https://www.zybuluo.com/xzyxzy/note/1041449 一.概述 单调队列单调栈是很基础的数据结构,常用来优化一些东西比如说优化DP 那么大概意思就是在栈或队列中按照某关键字单调维护信息,从而实现一些功能 其实很久之前接触过单调队列和单调栈,但一直没有刷题,趁这两天被LCT弄晕的时候复习下这些 先看题 二.题单 普及- [x] P1886 滑动窗口 https://www.luogu.org/problemnew/

【NOIP数据结构专项】单调队列单调栈

[洛谷P1901 ]发射站 http://www.luogu.org/problem/show?pid=1901 题目描述 某地有 N 个能量发射站排成一行,每个发射站 i 都有不相同的高度 Hi,并能向两边(当 然两端的只能向一边)同时发射能量值为 Vi 的能量,并且发出的能量只被两边最近的且比 它高的发射站接收. 显然,每个发射站发来的能量有可能被 0 或 1 或 2 个其他发射站所接受,特别是为了安 全,每个发射站接收到的能量总和是我们很关心的问题.由于数据很多,现只需要你帮忙计 算出接收

小Z爱序列(NOIP信(sang)心(bin)赛)From Fall_Dream(粗制单调队列&amp;单调栈的算法解析)

原题: 小Z最擅长解决序列问题啦,什么最长公共上升然后下降然后上升的子序列,小Z都是轻松解决的呢. 但是小Z不擅长出序列问题啊,所以它给了你一道签到题. 给定一个n个数的序列ai,你要求出满足下述条件的点对的数量. 假设点对是(i , j),max(l,r)是[l,r]当中最大的ai的值. 这个点对满足条件当且仅当i+1<j 且 ai < max(i+1,j-1) < aj 为了简单,保证输入的是一个1-n的排列.相信你已经会做了吧? 输入/输出格式 输入数据第一行有一个数字n,然后第二

单调队列&amp;单调栈归纳

单调队列 求长度为M的区间内的最大(小)值 单调队列的基本操作,也就是经典的滑动窗口问题. 求长度为M的区间内最大值和最小值的最大差值 两个单调队列,求出长度为M的区间最大最小值的数组,分别求最大最小值. 求边长为a的正方形内最大值和最小值的最大差值([HAOI2007]理想的正方形) 一个大体的思路是先分别求出以i,j为左上端点的边长为a的矩形中的最大值和最小值.那么该怎么做?,先用单调队列求出一个点右边a个单位的最大值,形成一个新矩阵,再求出新矩阵下边a个单位的最大值.然后最小值再求一边,最

单调队列/单调栈入门详解+题目推荐

以前一直以为这两个是很高级的东西,这段时间用到了才开始学,发现实际上非常简单 下面我们以单调队列为例进行讲解,单调栈自行类比 顾名思义 单调队列这个名字就指明了它的性质--单调性 我们来看一道例题--滑动窗口 题面在此不再赘述,大意就是有一个长度为\(n\)的数列,一个长度为\(k\)的窗口,输出窗口位于每个位置下的下的最大最小值 嗯,题目很好理解,st表或者线段树过的先别说话,我们来看看另一种方法 我们维护一个长度为k的队列,使得队列的开头为答案,那么我们每次只需要输出开头就好了.这个想法很好

单调队列 单调栈

建议不了解STL的读者先了解几个基本的队列的STL.这也是单调队列和单调栈一般都会用到的. 单调队列:建立一个队列,使队列一直具有单调性(满足单调递增或者单调递减),时间复杂度O(N). 那么我们应该如何做到"使队列一直具有单调性"呢? 以单调递增为例,我们O(N)扫描整个序列,每扫描到一个元素: 1 如果该元素大于等于队列末尾元素,则直接入队; 2 而如果该元素小于已有队列的末尾元素,即不满足单调递增,则使队列中的末尾元素出队,直到该元素符合入队条件,然后入队. 如果只到这里,那么我

[hdu1506]单调队列(栈)

题意:http://acm.hdu.edu.cn/showproblem.php?pid=1506看图一目了然.两个方向单调队列维护下. 1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstdlib> 5 #include <cstring> 6 #include <map> 7 #include <queue> 8

模板 - 数据结构 - 单调队列/单调栈

一道例题,给定一串数字,求每连续k个数字的最大.最小值. 思路:初始化一个初始长度为k的单调队列,按从左到右加入元素,同时满足这个队列中的元素是递减的(也就是假如某个数被两个距离不超过k的大于他的数夹着,他会被从队尾调出队列).得到最大值. 向右移动一格,假如队首离开范围,出队.往队尾加入元素前,把队尾的所有比它小的元素全部出队. 得到新的最大值. #include<bits/stdc++.h> using namespace std; struct Monotone_Queue{ deque

单调队列、单调栈、优先队列模板

目录 单调栈.单调队列及优先队列 1.单调队列 2.单调栈 3.优先队列 单调栈.单调队列及优先队列 1.单调队列 单调队列的描述:指队列中元素之间关系具有单调性,而且队首和队尾都可以出队,但是只有队尾可以进行入队操作.其重要作用是找到前n个后者后n个数的最值. 其具体操作是:假设单调队列是单调递减队列,假设在插入元素v时,将队列尾部的元素同v比较,如果队列尾部的元素不大于元素v,我们直接删除队尾元素,再将队尾元素与v比较,直至队尾元素比v大,这个时候我们将v插入队尾.其实现代码如下: int

单调队列

先放上luogu的题目链接--滑稽窗口 然后我们再来讲单调队列 单调队列是指这样一种队列:在队列中的元素为单调递增状态或单调递减状态. 例如1 2 3 4 5和9 2 1都是单调队列,但1 2 2 3 4和4 3 4 5就不是单调队列. 但普通队列明显是维持不了单调队列的性质的. 为了维持单调队列的单调性质,我们只好想一些方法.方法就是修改队列的性质.单调队列不仅队头可以出队,队尾也可以出队. 比如说有一个单调队列是 1 3 7 8 现在突然要从队尾进来一个6如果单纯的把6插进队尾的话,那这个队