学习目标:
我们再数据结构中学习过一些常用的存储方式:栈和队列。但是我们对于他们的应用也比较简单。今天我们来看一下第一个——栈的高级应用——单调栈!
学习内容:
关于栈我们是比较了解的,先进后出。但单调栈呢?
顾名思义,单调栈就是栈内元素的增长变化是单调的,可增可减,可非增,可非减。只需要我们在进栈的时候对它进行一个判断即可。这就是单调栈的定义,那我们如何来应用它呢?让我们以举个栗子:
有一个正整数的数组,化为直方图,求此直方图包含的最大矩形面积。例如 2,1,5,6,2,3,对应的直方图如下:
面积最大的矩形为5,6组成的宽度为2的矩形,面积为10。
Input
第1行:1个数N,表示数组的长度(0 <= N <= 50000) 第2 - N+1行:数组元素A[i]。(1 <= A[i] <= 10^9)
Output
输出最大的矩形面积
Sample
Inputcopy Outputcopy 6 2 1 5 6 2 3 10
看完了题目,我们可能还会想:“这玩意和单调栈有什么关系?直接暴力不就行了”。 害,这不就偏离我们的中心思想了么,如果全都去暴力求解,还要我们学习算法干什么?我们当然是要追求更快更优,而且这道题你用暴力必定超时,不信邪你就点击下方链接自己提交一下试试。最大面积https://vjudge.net/contest/480490#problem/A
当然,大家看完后也可以自己去提交一下这道题目,手动一边好过眼扫千遍。
看完题目后,小编来为大家捋一下思路:给了我们一个数组,按照每个数字的大小做出一个柱形图,让我们求在这个不规则的图形里找到一个最大面积的矩形求出它的面积。
既然是求最值问题,那么就一定是有一个临界条件,使得最大或最小。那么我们思考这个临界条件是什么?
矩形的面积公式是长*宽,在这里也叫底*高,底就是我们子数组的长度,高呢就是子数组里的最小值。无论是单从底入手还是单从高入手,我们都不能直接求得最大值。那么我们就来想一下,从局部到整体,先从某一个子集来考虑,固定这个子集的一端,先向右扩张,要求最大值,我们要扩张到什么时候?是不是应该到某个值,小于我们这个端点的值,向左也是一样,这时我们的这个区间,是不是就是在高为端点值的情况下,所能取得的最大的底,那么也就是在高固定的情况下取得的最大面积,接下来我们只要枚举高,也就是遍历数组,用同样的方法求得面积,最后取得最大的那个是不是就可以了?
对的,这就是有两个变量时,我们常采用的控制变量法,先控制一个变量不变,求得最大值,然后继续向下,求得每一个的最大值,最终就能求得最终的最大值。
下面我们来看代码如何完成:我们可以采用单调不减栈的方式,先向右端,只要满足大于等于的条件,就可以一直进栈,直到不满足,然后以相同的方法向另一端扩张。然后判断是否大于sum。栈的话小编用数组模拟栈,因为其重要调用栈中元素,若用c++的vector就只能得到栈顶元素,因此小编定义数组stk和栈顶指针top来模拟栈。大家有其他方法欢迎留言讨论。下面代码呈上:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=50000;
ll num[N],stk[N];
ll max(ll a,ll b){
return a>b?a:b;
}
int main() {
ll n,i,j,top=0,sum=0;
scanf("%d",&n);
for(i=0;i<n;i++){
scanf("%d",&num[i]);
}
for(i=0;i<n;i++){
stk[0]=num[i];
top=0;
j=i+1;
while(stk[0]<=num[j] && j<n){
stk[++top]=num[j++];
}
j=i-1;
while(stk[0]<=num[j] && j>0){
stk[++top]=num[j--];
}
sum=max(sum,(top+1)*stk[0]);
}
printf("%lld",sum);
return 0;
}
要注意数据范围,这里的数是10^9,因此小编用long long来定义。
每天坚持是一件很累的事,即使很慢,但也不要停止。由于过年,小编停更了几天,从今天开始,小编继续每日一题。欢迎大家及时督促,大家一起加油!
大家记得点赞收藏,防止找不到哦。
欢迎大家订阅小编的每日一题专栏,会每天更新,都是用心准备的哦!
若有不同思路,欢迎评论区留言,看到必回,Goodnight!