题意:
给一个直方图,求直方图中最大矩形的面积。例如,下图直方图的高度从左到右分别是2, 1, 4, 5, 1, 3, 3, 他们的宽都是1,其中最大的矩形是阴影部分。
输入要求:
输入包含多组数据。每组数据先输入一个n(表示直方图中小矩形的个数)且 1 <= n <= 100000,再输入n个数
h
1
h_1
h1,…,
h
n
h_n
hn,满足 0 <=
h
1
h_1
h1 <= 1000000000。输入数据直到 n=0 时结束。
输出要求:
每组测试数据输出一行一个整数表示答案。
Sample input:
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
Sample output:
8
4000
思路:
- 要求出合成矩形的面积,首先,需要求出对于每个直立小矩形,往左和往右最远能到的不低于该矩形高度的横坐标,两者相减即合成矩形的宽,直立矩形的高就是合成矩形的高,由此求出合成矩形的面积。通过比较每个横坐标对应的合成矩形的面积,即可求出最大矩形的面积。
- 为了求出每个直立小矩形左边和右边第一个小于其高度的点,需要用到单调矩阵的方法,单调矩阵使用了自己定义的数组 st 和栈顶 top 来实现。入栈时 top++,出栈时 top–,由此实现栈;入栈时还需判断栈顶元素与入栈元素大小,由此实现单调栈。
- 定义好单调栈之后,通过两次单调栈的使用(从左往右、从右往左)来求出每个直立小矩形左右两个边界的横坐标,存入数组中,以下为其中一次使用,求出的右边界的横坐标。
for (int i = 0; i < n; i++) {
while (top > -1 && st[top].hi > a[i]) {
r[st[top].x] = i;
top--;
}
top++;
st[top].x = i;
st[top].hi = a[i];
}
- 求出左右边界横坐标之后,即可遍历判断,求出最大矩形的面积。但还有需要注意的问题:直立矩形高度的最大值为 1e9,且 n 的最大值为 1e5,故答案最大值可能达到 1e14,超出了 int 整型的范围,所以需要使用 long long 型变量才可防止 wa。
代码:
#include <iostream>
#include <algorithm>
using namespace std;
long int a[100005];
long int n;
struct p {
long int x, hi;
p() {}
p(int x_, int h_) {
x = x_;
hi = h_;
}
};
p st[100005]; //栈数组
int top = -1; //栈顶
long long int l[100005]; //左边第一个小于其高度的点
long long int r[100005]; //右边第一个小于其高度的点
int main()
{
while (cin >> n) {
if (n == 0) break;
for (int i = 0; i < n; i++)
cin >> a[i];
for (int i = 0; i < n; i++) {
l[i] = 0;
r[i] = n;
}
for (int i = 0; i < n; i++) {
while (top > -1 && st[top].hi > a[i]) {
r[st[top].x] = i;
top--;
}
top++;
st[top].x = i;
st[top].hi = a[i];
}
top = -1;
for (int i = n-1; i >= 0; i--) {
while (top > -1 && st[top].hi > a[i]) {
l[st[top].x] = i + 1;
top--;
}
top++;
st[top].x = i;
st[top].hi = a[i];
}
top = -1;
long long int maxS = 0;
for (int i = 0; i < n; i++) {
maxS = max(maxS, (r[i] - l[i])*a[i]);
}
cout << maxS << endl;
}
}