单调栈的应用----直方图中矩阵最大面积

单调栈

简单定义:单调栈就是从栈顶到栈底,元素满足单调性;
如单调递增栈:
在这里插入图片描述
如何维护单调栈?
以维护单调递增栈为例,当一个元素a入栈的时候,判断a和栈顶元素的大小,如果栈顶元素小于等于a,那么就弹出栈顶元素,直到栈顶元素大于a(或者栈为空)。
例如,把7放入栈当中:
在这里插入图片描述
单调栈的用处
*可以判断一个数组中,任何一个元素左边(右边)第一个比自己大(小)的元素。*比如上面的例子,如果数组是10 8 6 7;根据我们单调递增栈的维护,7左边第一个比自己大的元素是7(6比自己小在自己入栈的时候被弹出去了)

一个题目

给一个直方图,求直方图中的最大矩形的面积。例如,下面这个图片中直方图的高度从左到右分别是2, 1, 4, 5, 1, 3, 3, 他们的宽都是1,其中最大的矩形是阴影部分。

在这里插入图片描述
小反思:这个题目里面小矩阵个数范围:1-100000;
矩阵高度范围:0 <= hi <= 1000000000
所以矩阵面积int可能会溢出,所以要用long long int

思路:

遍历直方图的每一个小矩形,找到它左边一个小于它的矩阵编号,再找到右边第一个小于它的矩阵编号,就可以找到它左右两边的边界,算出最大的长。
要实现这个功能就可以用上面的单调栈,维护单调递减栈,每放入一个矩形高度的时候,就可以找到它左边第一个小于它的元素下标。然后逆着放入矩阵高度数组,就可以找到右边第一个小于它的元素下标。

全部代码

#include<stdio.h>
#include<stack>
using namespace std;
long long int n,a[100000],l[100000],r[100000];
struct P
{
	long long int value;long long int xiabiao;
};
stack<P> ST;
void fun1()//维护单调递减栈
{
	while(!ST.empty())      ST.pop();
	l[0]=-1;
	P p;p.value=a[0];p.xiabiao=0;
	ST.push(p);
	for(int i=1;i<n;i++)
	{
		while(!ST.empty()&&a[i]<=ST.top().value)
		{
			ST.pop();
		}
		if(ST.empty())//如果是空,则左边的都比它大
			l[i]=-1;
		else  l[i]=ST.top().xiabiao;
		
		P now;now.value=a[i];now.xiabiao=i;
		ST.push(now);
	}

}

void fun2()//维护单调递减栈
{
	while(!ST.empty())      ST.pop();
	r[n-1]=n;
	P p;p.value=a[n-1];p.xiabiao=n-1;
	ST.push(p);
	for(int i=n-2;i>=0;i--)
	{
		while(!ST.empty()&&a[i]<=ST.top().value)
		{
			ST.pop();
		}
		if(ST.empty())//如果是空,则左边的都比它大
			r[i]=n;
		else  r[i]=ST.top().xiabiao;

		P now;now.value=a[i];now.xiabiao=i;
		ST.push(now);
	}

}

int main()
{
	while(scanf("%lld",&n))
	{
		long long int sum=0;
		if(n==0)    break;
		for(int i=0;i<n;i++)
			scanf("%lld",&a[i]);//输入直方图
		//对于每个小矩形,向左找第一个小于它的,向右找第一个小于它的
		//维护单调递减栈
		fun1();
		fun2();
		for(int i=0;i<n;i++)
		{
			long long int result=((r[i]-l[i])-1)*a[i];
			if(result>sum)  sum=result;
		}
		printf("%lld\n",sum);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值