数据结构——单调栈,单调队列

参考来自:https://endlesslethe.com/monotone-queue-and-stack-tutorial.html#i-2

单调栈

一个单调递增栈的例子:
进栈元素分别为3,4,2,6,4,5,2,3

第i步 操作 结果
1 3进栈 3
2 3出栈,4进栈 4
3 2进栈 4 2
4 2、4出栈,6进栈 6
5 4进栈 6 4
6 4出栈,5进栈 6 5
7 2进栈 6 5 2
8 2出栈,3进栈 6 5 3

对于一个元素i:

  • 左边区间第一个比它大(小)的元素;
  • 确定这个元素是否为区间最值
  • 右边区间第一个大于它的值
  • 到右边区间第一个大于它的值的距离
  • 确定以该元素为最值得最长区间

代码

//在“尾部”添加元素
while (r != 0 || ms[r] <= x) r--;
ms[++r] = x;

//查询栈顶元素
if (r != 0) printf("%d\n", ms[r]);
else printf("-1");

这里可以把x换成元素x得下标压入栈

单调队列

一个递增单调队列的例子
队列大小不能超过3,入队元素依次为3,2,8,4,5,7,6,4

第i步 操作 结果
1 3入队 3
2 3从队尾出队,2入队 2
3 8入队 2 8
4 8从队尾出队,4入队 2 4
5 5入队 2 4 5
6 2从队头出队,7入队 4 5 7
7 7从队尾出队,6入队 4 5 6
8 6、5、4从队尾出队,4入队 4
  • 可以查询区间最值(不能维护区间第k大)
  • 优化DP

代码

//在“尾部”添加元素x
while (l != r && mq[r] <= x) r--;
mq[++r] = x;

//查询队首元素
if (l != r) printf("%d\n", mq[l+1]);
else printf("-1\n");

//弹出队首元素
if (l != r) l++;

下面直接见练习题(持续收集)

1. POJ 2823 Sliding Window

链接:POJ 2823
**题意:**就是给你n 和 k,表示元素的个数, 以及区间长度, 要求输出从1到n-k为起始下标的每个长度为k的区间的最小值输出,以及最大值输出。
**题解:**这里用单调队列写,记录的是下标,并且判断一下当前的最值的下标在不在当前的区间内就可,具体看代码。
代码:

//#include <bits/stdc++.h>
#include <cstdio>
#include <iostream>

using namespace std;

const int maxn = 1e6+50;
int n, len, l, r;
int a[maxn];
int mq[maxn];

int main()
{
    scanf("%d %d",&n, &len);
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
    }
    ///1 3 -1 -3 5 3 6 7
    ///3 2 8 4 5 7 6 4
    l = 0, r = 0;
    for(int i = 1; i <= n; i++){
        while(l != r && a[mq[r-1]] >= a[i]){
            r--;
        }
        mq[r++] = i;
        while(mq[l] <= i - len) l++;
        //for(int j = l; j < r; j++)
        //    cout << a[mq[j]] << " ";
        //cout << endl;
        if(i >= len) cout << a[mq[l]] << " ";
    }
    cout << endl;
    l = 0, r = 0;
    for(int i = 1; i <= n; i++){
        while(l != r && a[mq[r-1]] <= a[i]) r--;
        mq[r++] = i;
        while(mq[l] <= i - len) l++;
        if(i >= len) cout << a[mq[l]] << " ";
    }
    cout << endl;
    return 0;
}

2.POJ 3250 Bad Hair Day

链接:POJ 3250
**题意:**给你n头奶牛的高度,从左到右依次排列,都向右看,现在要你求每一头奶牛向右看可以看到的奶牛总数。
**题解:**单调队列简单题
代码:

#include <iostream>
#include <cstdio>

using namespace std;

const int maxn = 1e6+60;
long long n, a[maxn];
long long h[maxn], w[maxn], cur = 0;
long long ans = 0;
const int INF = 0x3f3f3f3f;

void solve()
{
    h[0] = INF+1;
    for(int i = 1; i <= n+1; i++)
    {
        if(a[i] < h[cur])
        {
            h[++cur] = a[i];
            w[cur] = cur-1;
        }
        else
        {
            while(a[i] >= h[cur])
            {
                ans += w[cur];
                h[cur--] = 0;
                if(cur == 0)
                    break;
                //cur--;
            }
            h[++cur] = a[i];
            w[cur] = cur-1;
        }
        if(cur == 0)
            return;
    }
}

int main()
{
    scanf("%lld", &n);
    for(int i = 1; i <= n; i++)
        scanf("%lld", &a[i]);
    a[n+1] = INF;
    solve();
    cout << ans << endl;
    return 0;
}

发布了23 篇原创文章 · 获赞 6 · 访问量 802

猜你喜欢

转载自blog.csdn.net/wxy2635618879/article/details/104338284