题目描述:
给一个直方图,求直方图中的最大矩形的面积。
例如,下面这个图片中直方图的高度从左到右分别是
2, 1, 4, 5, 1, 3, 3,
他们的宽都是1,
其中最大的矩形是阴影部分。
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)
利用单调栈的性质,正反向两次遍历,找出每个高的左右端点。
- 正向,利用单调非增栈的性质,当栈顶元素的高 > 待入栈元素,pop掉栈顶元素,直到栈顶元素 <= 待入栈元素。
- 反向,操作与1基本相同,只是从后往前遍历的。
- 数组存储,因为我们是根据高遍历找的左右端点,所以在存储的时候你可以用一个结构体存储:
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;
}
}