单调队列和单调栈学习笔记

单调栈:

单调栈是指一个栈内部的元素是具有严格单调性的一种数据结构,分为单调递增栈和单调递减栈。

单调栈有两个性质:
1.满足从栈顶到栈底的元素具有严格的单调性
2.满足栈的后进先出特性越靠近栈底的元素越早进栈
元素进栈过程
对于一个单调递增栈来说 若当前进栈的元素为 a 如果a < 栈顶元素则直接将a 进栈 如果 a >= 当前栈顶元素则不断将栈顶元素出栈知道满足 a < 栈顶元素
图片说明:
这里写图片描述

单调队列:
单调队列是指一个队列内部的元素具有严格单调性的一种数据结构,分为单调递增队列和单调递减队列。

单调队列满足两个性质

1.单调队列必须满足从队头到队尾的严格单调性。

2.排在队列前面的比排在队列后面的要先进队。

元素进队列的过程对于单调递增队列,对于一个元素a 如果 a > 队尾元素 那么直接将a扔进队列 如果 a <= 队尾元素 则将队尾元素出队列 知道满足 a 大于队尾元素即可;
图片说明

这里写图片描述

单调队列的模板及注释(模板只是参考,要根据题会改,会用某个部分)

【1】求最值
例题:poj 2823
http://poj.org/problem?id=2823
题意:求多个区间最值
代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
    int x,y;
}v[1010000]; //x表示值,y表示位置 可以理解为下标
int a[1010000],n,m,mx[1010000],mn[1010000];
void getmin()
{
    int i,head=1,tail=0;// 默认起始位置为1 因为插入是v[++tail]故初始化为0
    for(i=1;i<m;i++)
    {
        while(head<=tail && v[tail].x>=a[i]) tail--;
        v[++tail].x=a[i],v[tail].y=i;
                // 根据题目 前m-1个先直接进入队列
    }
    for(;i<=n;i++)
    {
        while(head<=tail && v[tail].x>=a[i]) tail--;
        v[++tail].x=a[i],v[tail].y=i;
        while(v[head].y<i-m+1) head++;
        mn[i-m+1]=v[head].x;
               // 道理同上,当然了 要把已经超出范围的从head开始排出
               //  然后每个队首则是目前m个数的最小值
    }
}
void getmax() //最大值同最小值的道理,只不过是维护的是递减队列
{
    int i,head=1,tail=0;
    for(i=1;i<m;i++)
    {
        while(head<=tail && v[tail].x<=a[i]) tail--;
        v[++tail].x=a[i],v[tail].y=i;
    }
    for(;i<=n;i++)
    {
        while(head<=tail && v[tail].x<=a[i]) tail--;
        v[++tail].x=a[i],v[tail].y=i;
        while(v[head].y<i-m+1) head++;
        mx[i-m+1]=v[head].x;
    }
}
int main()
{
    int i,j;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    getmin();
    getmax();
    for(i=1;i<=n-m+1;i++)
    {
        if(i==1)printf("%d",mn[i]);
        else printf(" %d",mn[i]);
    }
    printf("\n");
    for(i=1;i<=n-m+1;i++)
    {
        if(i==1)printf("%d",mx[i]);
        else printf(" %d",mx[i]);
    }
    printf("\n");

    return 0;
}

【2】求最值,有多个操作,包括删队首(删去最大值,最大值改变的情况),入队,查询;
fzu 1894
http://acm.fzu.edu.cn/problem.php?pid=1894

世博会马上就要开幕了,福州大学组织了一次志愿者选拔活动。
参加志愿者选拔的同学们排队接受面试官们的面试。参加面试的同学们按照先来先面试并且先结束的原则接受面试官们的考查。
面试中每个人的人品是主要考查对象之一。(提高人品的方法有扶老奶奶过街,不闯红灯等)
作为主面试官的John想知道当前正在接受面试的同学队伍中人品值最高的是多少。于是他请你帮忙编写一个程序来计算。
Input
输入数据第一行为一整数T,表示有T组输入数据。
每组数据第一行为”START”,表示面试开始
接下来的数据中有三种情况:
输入 含义
1 C NAME RP_VALUE 名字为NAME的人品值为RP_VALUE的同学加入面试队伍。(名字长度不大于5,0 <= RP_VALUE <= 1,000,000,000)
2 G 排在面试队伍最前面的同学面试结束离开考场。
3 Q 主面试官John想知道当前正在接受面试的队伍中人品最高的值是多少。
最后一行为”END”,表示所有的面试结束,面试的同学们可以依次离开了。
所有参加面试的同学总人数不超过1,000,000
Output
对于每个询问Q,输出当前正在接受面试的队伍中人品最高的值,如果当前没有人正在接受面试则输出-1。
Sample Input
2
START
C Tiny 1000000000
C Lina 0
Q
G
Q
END
START
Q
C ccQ 200
C cxw 100
Q
G
Q
C wzc 500
Q
END
Sample Output
1000000000
0
-1
200
100
500
Hint
数据较大建议使用scanf,printf 不推荐使用STL
注意:结构体里的i要用,虽然我不明白为什么,不加样例也过了,但是WA;

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
struct node
{
    int x,i;
} v[1000005],a[1000005];
char s[100];
char s1[100];

int main()
{
    int t;
    scanf("%d",&t);
    int p=0,w=t;
    while(t--)
    {
        int j=1,i=0;
        memset(v,0,sizeof(v));
        int head=1,tail=0;
        scanf("%s",s);
        while (scanf("%s",s)&&strcmp(s,"END")!=0)
        {
            if(s[0]=='C')
            {
                scanf("%s",s1);
                i++;
                scanf("%d",&a[i].x);
                a[i].i=i;
                while(head<=tail&&v[tail].x<a[i].x) tail--;
                v[++tail]=a[i];
            }
            else if(s[0]=='Q')
            {

                if(head<=tail)
                {
                    printf("%d\n",v[head].x);
                }
                else
                {
                    printf("-1\n");
                }
            }
            else if(s[0]=='G')
            {
                j++;
                if(v[head].i<j)
                    head++;
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/wuxiaowu547/article/details/81321969