【C++】单调队列 & 单调栈

目录

介绍

双向队列

单调性的讨论

单调栈

例题

例题讲解

Cow Line

滑动Windows

分析

代码

最大矩形main积

分析

代码

 

 

介绍

双向队列

先提前介绍一下一个东西:deque

话说这个东西和list很像啊,只是少了一个插入insert操作罢了……

不过为什么还要创造deque这种东西呢? 原因是,list太慢了……

转回正题,deque的头文件是

#include
#include
using namespace std;

 以上两个都可以,但必须要加using namespace std;(bits/stdc++.h瑟瑟发抖)

至于函数……

dequedq;
dq.push_back(1);
dq.push_front(2);
dq.push_front(3);
dq.push_back(4);
// dq : 3 2 1 4
dq.pop_back();
// dq : 3 2 1
dq.pop_front();
// dq : 2 1

这里就针对STL不多说了吧……

单调性的讨论

单调队列,即单调递减或单调递增的一个队列,不过,它不是一个普通的FIFO队列,而是双向队列。

单调栈,单调递增或单调递减减的栈,跟单调队列差不多,但是只用到它的一端。——百度百科

就这么短,没别的了

单调栈

直接上模板代码吧

template
struct DownStack{
    T sta[1001];
    int bck;
    inline T front() {
        return sta[1];
    }
    inline T back() {
        return sta[bck];
    }
    inline void pop_back() {
        bck--;
    }
    inline bool empty() {
        return bck;
    }
    inline int size() {
        return bck;
    }
    inline void push_back(T a) {
        while(back() ds;
inline void output() {
    for(register int i=ds.begin();i

输出结果为

1
1 -1
1 0

 可以看出,输出的每一个序列都是单调下降的。单调上升就可以直接类比得出。

忽略单调队列

例题

  1. Cow Line(相当于deque模板)
  2. 重要的板题1:滑动Windows
  3. 重要的板题2:最大矩形main积(竟然是蓝题)

例题讲解

Cow Line

单纯的模拟题,就不多说了吧

#include
#include
using namespace std;
int read()
{
    int a=0,f=1; char c=getchar();
    while(c<'0'||c>'9') { if(c=='-') f=-f; c=getchar(); }
    while(c>='0'&&c<='9') a=a*10+c-'0',c=getchar();
    return a*f;
}
int S=read(),k;
char s[2];
dequedq;
int main()
{
    while(S--)
    {
        scanf("%s",s);
        if(s[0]=='A')
        {
            scanf("%s",s);
            if(s[0]=='L') dq.push_front(++k);
            else dq.push_back(++k);
        }
        else
        {
            scanf("%s",s);
            int t=read();
            while(t--)
                if(s[0]=='L') dq.pop_front();
                else dq.pop_back();
        }
    }
    while(!dq.empty())
    {
        printf("%d\n",dq.front());
        dq.pop_front();
    }
}

滑动Windows

这可不是普通的板题,而是一道十分重要的板题! 


分析

我们可以对两种询问分别运用两个不同单调性的队列来维护。

对于最小值而言,就维护一个单调上升的队列;反之,对于最大值而言,就可以维护一个单调下降的队列。

在这种时候,队列的头元素就是要求的最大/最小值。

也许你会问,一直只删除后面的元素,那不是只有pop完了过后才会更改头元素吗?

对,不过,你忘了一件事:窗口的大小为k,只能有k个元素在窗口里。

所以,还要维护开头的下标,如果下标与当前的i的差距超过了k的话,就可以直接pop掉。

当然,推荐用手动模拟双向队列,这样时间复杂度要好一些。就怕某些OJ卡list和deque

同理,为了缩小空间,可以使用同一个队列来进行两种操作,中间记得清空队列,即初始化和头尾均置为初始值。

代码

#include
#include
#define reg register
template 
inline T read() {
    T a=0; char c=getchar(),f=1;
    while(c<'0'||c>'9') {
        if(c=='-') f=-f;
        if(c==-1) return c;
        c=getchar();
    }
    while(c>='0'&&c<='9') a=(a<<1)+(a<<3)+(c^48),c=getchar();
    return a*f;
}
template 
inline int write(T x) {
    if(x<0) x=(~x)+1, putchar('-');
    if(x/10) write(x/10);
    return putchar(x%10|48);
}
template 
inline int write(T x,char c) {
    return write(x)&&putchar(c);
}
template 
inline T Max(T a,T b) { return a>b?a:b; }
template 
inline T Min(T a,T b) { return a
inline T Abs(T a) { return a<0?-a:a; }
const int MAXN=1000001;
int n=read(),k=read();
struct node{
    int it,val;
    node(int I=0,int V=0) { it=I,val=V; }
}dq[MAXN];
int fnt,bck;
int a[MAXN];
int main() {
    for(reg int i=1;i<=n;i++) {
        a[i]=read();
    }
    memset(dq,0x3f,sizeof dq);
    fnt=1; bck=0;
    for(reg int i=1;i0&&dq[bck].val>a[i])
            bck--;
        dq[++bck]=node(i,a[i]);
    }
    for(reg int i=k;i<=n;i++) {
        if(dq[fnt].it+k==i)
            fnt++;
        while(fnt<=bck&&bck>0&&dq[bck].val>a[i])
            bck--;
        dq[++bck]=node(i,a[i]);
        write(dq[fnt].val);
        if(i!=n) putchar(' ');
    }
    putchar(10);
    memset(dq,-0x3f,sizeof dq);
    fnt=1; bck=0;
    for(reg int i=1;i0&&dq[bck].val0&&dq[bck].val

最大矩形main积

分析

我们思考一下如果i为子矩阵中高度最小的矩阵

那么i所能构造的最大子矩阵也就是i左边第一个比它矮的

到i右边第一个比它矮的距离再乘上i的高度

那么原题便转换成了max((r_i-l_i)*h_i)(1\leqslant i\leqslant n)l_i表示i左边第一个比它矮的,r_i表示右边第一个比它矮的,h_i表示第i个的高度)

所以,对于一个任意的i来说,最主要的就是求l_ir_i

不过如何求?(都在单调队列和单调栈这个区块上,你觉得还有什么方法)

我们可以维护一个单调递增的栈 ,那么装进h_i时,便需要弹出栈中不符合的元素

对于一个元素a_i来说,入栈的时候,l_i就是i

问题是——r_i怎么求?

——出栈的时候(设当前遍历到的元素为j),r_i=j-1

原因很简单,当当前元素出栈时,h_j<h_i

至于具体处理,用一个结构体储存一下进栈时的id即可

代码

#include
#include
#include
#define reg register
typedef long long LL;
template 
inline T read() {
    T a=0; char c=getchar(),f=1;
    while(c<'0'||c>'9') {
        if(c=='-') f=-f;
        if(c==-1) return c;
        c=getchar();
    }
    while(c>='0'&&c<='9') a=(a<<1)+(a<<3)+(c^48),c=getchar();
    return a*f;
}
template 
inline int write(T x) {
    if(x<0) x=(~x)+1, putchar('-');
    if(x/10) write(x/10);
    return putchar(x%10|48);
}
template 
inline int write(T x,char c) {
    return write(x)&&putchar(c);
}
template 
inline T Max(T a,T b) { return a>b?a:b; }
template 
inline T Min(T a,T b) { return a
inline T Abs(T a) { return a<0?-a:a; }
LL n;
LL h[100002];
struct node{
    LL val,id;
    node(){}
    node(LL V,LL ID) { val=V; id=ID; }
};
LL maxn;
LL l[100002],r[100002];
node sta[100002]={};
int top;
inline void solve() {
    top=0;
    maxn=-0x3f3f3f3f;
    memset(l,0,sizeof l); memset(r,0,sizeof r);
    for(reg int i=1;i<=n;i++) h[i]=read();
    h[n+1]=0;
    for(reg int i=1;i<=n+1;i++) {
        while(top) {
            node t=sta[top];
            if(t.val())&&n!=-1) {
        solve();
    }
}

 

 

 

 

 

你可能感兴趣的:(My,OI)