单调栈

题目链接

acwing830. 单调栈
P5788 【模板】单调栈
acwing131. 直方图中最大的矩形

acwing830. 单调栈

题目描述

给定一个长度为 \(N\) 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 \(−1\)

输入格式

第一行包含整数 \(N\),表示数列长度。

第二行包含 \(N\) 个整数,表示整数数列。

输出格式

共一行,包含 \(N\) 个整数,其中第 \(i\) 个数表示第 \(i\) 个数的左边第一个比它小的数,如果不存在则输出 \(−1\)

数据范围

\(1≤N≤10^5\)
\(1≤数列中元素≤10^9\)

输入样例:

5
3 4 2 7 5

输出样例:

-1 3 -1 2 2
  • 时间复杂度:\(O(n)\)

代码

#include<bits/stdc++.h>
using namespace std;
int n;
int a[100010];
stack<int> stk;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    {
        while(stk.size()&&a[stk.top()]>=a[i])stk.pop();
        if(stk.size())
            printf("%d ",a[stk.top()]);
        else
            printf("-1 ");
        stk.push(i);
    }
    return 0;
}

P5788 【模板】单调栈

题目描述

给出项数为 \(n\) 的整数数列 \(a_{1 \dots n}\)

定义函数 \(f(i)\) 代表数列中第 \(i\) 个元素之后第一个大于 \(a_i\) 的元素的下标,即 \(f(i)=\min_{i<j\leq n, a_j > a_i} \{j\}\)。若不存在,则 \(f(i)=0\)

试求出 \(f(1\dots n)\)

输入格式

第一行一个正整数 \(n\)

第二行 \(n\) 个正整数 \(a_{1\dots n}\)

输出格式

一行 \(n\) 个整数 \(f(1\dots n)\) 的值。

输入

5
1 4 2 3 5

输出

2 5 4 5 0

说明/提示

【数据规模与约定】

对于 \(30\%\) 的数据,\(n\leq 100\)

对于 \(60\%\) 的数据,\(n\leq 5 \times 10^3\)

对于 \(100\%\) 的数据,\(1 \le n\leq 3\times 10^6,1\leq a_i\leq 10^9\)

  • 时间复杂度:\(O(n)\)

代码

#include<bits/stdc++.h>
using namespace std;
int n,a[3000010];
stack<int> stk,stk_res;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=n;i;i--)
    {
        while(stk.size()&&a[i]>=a[stk.top()])stk.pop();
        if(stk.size())
            stk_res.push(stk.top());
        else
            stk_res.push(0);
        stk.push(i);
    }
    while(stk_res.size())
        printf("%d ",stk_res.top()),stk_res.pop();
    return 0;
}

acwing131. 直方图中最大的矩形

题目描述

直方图是由在公共基线处对齐的一系列矩形组成的多边形。

矩形具有相等的宽度,但可以具有不同的高度。

例如,图例左侧显示了由高度为 \(2,1,4,5,1,3,3\) 的矩形组成的直方图,矩形的宽度都为 \(1\)

image

通常,直方图用于表示离散分布,例如,文本中字符的频率。

现在,请你计算在公共基线处对齐的直方图中最大矩形的面积。

图例右图显示了所描绘直方图的最大对齐矩形。

输入格式

输入包含几个测试用例。

每个测试用例占据一行,用以描述一个直方图,并以整数 \(n\) 开始,表示组成直方图的矩形数目。

然后跟随 \(n\) 个整数 \(h_1,…,h_n\)

这些数字以从左到右的顺序表示直方图的各个矩形的高度。

每个矩形的宽度为 \(1\)

同行数字用空格隔开。

当输入用例为 \(n=0\) 时,结束输入,且该用例不用考虑。

输出格式

对于每一个测试用例,输出一个整数,代表指定直方图中最大矩形的区域面积。

每个数据占一行。

请注意,此矩形必须在公共基线处对齐。

数据范围

\(1≤n≤100000\),
\(0≤h_i≤1000000000\)

输入样例:

7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0

输出样例:

8
4000

解题思路

单调栈

我们利用单调栈找到每个矩形 \(k\) 左右第一个比它小的矩形 \(i,j\),则这个矩形形成的面积为 \(h[k]*(j-i-1)\),需要注意的是,单调栈里的每个元素的左边即为该元素左边比它小的第一个元素,同时通过这个单调栈还能求出右边的第一个元素
\(\color{red}{如何求解?}\)
遍历当前元素时,此时栈顶作为我们的基准矩形,其左边比它小的第一个元素即次栈顶元素,当当前元素比栈顶元素小时,当前元素就是右边第一个比栈顶元素小的第一个元素,\(\color{red}{为什么?}\)不妨假设在栈顶元素和当前元素中间还有一个比栈顶元素小的元素,则在当前元素之前,栈顶元素是会被更新的,与当前的栈顶元素矛盾,故当前元素就是栈顶元素右边第一个比栈顶元素小的元素。这里为使所有元素都出栈,增加了一个很小的元素~

  • 时间复杂度:\(O(n)\)

代码

#include<bits/stdc++.h>
using namespace std;
int h[100005],n;
stack<int> stk;
int main()
{
    while(scanf("%d",&n),n)
    {
        for(int i=0;i<n;i++)scanf("%d",&h[i]);
        h[n]=-1;
        while(stk.size())stk.pop();
        long long res=0;
        for(int i=0;i<=n;i++)
        {
            while(stk.size()&&h[i]<h[stk.top()])
            {
                int cur=stk.top();
                stk.pop();
                if(stk.size())res=max(res,1ll*h[cur]*(i-stk.top()-1));
                else
                    res=max(res,1ll*i*h[cur]);
            }
            stk.push(i);
        }
        printf("%lld\n",res);
    }
    return 0;
}
posted @ 2021-09-22 15:18  zyy2001  阅读(66)  评论(0编辑  收藏  举报