Week5—A—最大矩形(单调栈)

题目描述:

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

week5—A


Input:

输入包含多组数据。每组数据用一个整数n来表示直方图中小矩形的个数,你可以假定1 <= n <= 100000. 然后接下来n个整数h1, …, hn, 满足 0 <= hi <= 1000000000. 这些数字表示直方图中从左到右每个小矩形的高度,每个小矩形的宽度为1。 测试数据以0结尾。

sample input:

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

Output:

对于每组测试数据输出一行一个整数表示答案。

sample output:

	8
	4000

个人思路:

首先,面积 = 高 * 宽。
所以,得找到合适的高和宽,假设枚举每个矩形的高,然后找到其对应矩形的宽,
最后得到面积的最大值,但是复杂度太高。

这里引入一个新的数据结构,单调栈:
顾名思义:单调栈 = 单调 + 栈;
栈大家都不陌生,单调栈就是在栈的基础上,添加了单调的性质,也就是从栈底到栈顶元素是单调的。
详情请看一下这篇关于单调栈的介绍:[单调栈](https://blog.youkuaiyun.com/lucky52529/article/details/89155694)

利用单调栈的性质,正反向两次遍历,找出每个高的左右端点。

  1. 正向,利用单调非增栈的性质,当栈顶元素的高 > 待入栈元素,pop掉栈顶元素,直到栈顶元素 <= 待入栈元素。
  2. 反向,操作与1基本相同,只是从后往前遍历的。
  3. 数组存储,因为我们是根据高遍历找的左右端点,所以在存储的时候你可以用一个结构体存储:
struct zone {
	long long height,
		left, right, index;//左右两端、索引
};

至于为什么是long long,考虑极端情况

n = 100000; h[i] = 1000000000;

那么

	面积 = n * h = 1e14

所以 int 会WA掉。


代码实现:

#include<iostream>
#include<algorithm>
#include<stack>
#define MAX_N 100010
using namespace std;

struct zone {
	long long height,
		left, right, index;//左右两端、索引
};
zone a[MAX_N];//heights
zone st[MAX_N];//stack
int top = 0;
int main() {
	while (1) {
		int n;
		cin >> n;
		if (n == 0)return 0;

		for (int i = 1; i <= n; ++i) {
			cin >> a[i].height;		
			a[i].index = i;

			a[i].right = i;
			a[i].left = i - 1;
		}

		for (int i = 1; i <= n; ++i) {//正向
			while (top > 0 && st[top].height > a[i].height) { a[st[top].index].right = i - 1; top--; }
			st[++top] = a[i];
		}
		while (top > 0) { a[st[top].index].right = n; top--; }//clear stack

		for (int i = n; i >= 1; --i) {//反向
			while (top > 0 && st[top].height > a[i].height) { a[st[top].index].left = i; top--; }
			st[++top] = a[i];
		}
		while (top > 0) { a[st[top].index].left = 0; top--; }//clear stack

		long long max_s = 0;
		for (int i = 1; i <= n; ++i) {
			long long s = a[i].height * (a[i].right - a[i].left);
			max_s = max(max_s, s);
		}
		cout << max_s << endl;
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值