单调栈
题目链接
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\):
通常,直方图用于表示离散分布,例如,文本中字符的频率。
现在,请你计算在公共基线处对齐的直方图中最大矩形的面积。
图例右图显示了所描绘直方图的最大对齐矩形。
输入格式
输入包含几个测试用例。
每个测试用例占据一行,用以描述一个直方图,并以整数 \(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;
}