在这里先简单描述一下单调栈
单调栈:一种线性数据结构,栈内元素自栈顶到栈底满足单调性。有单调递增栈和单调递减栈。如果要加入栈的元素不满足单调性,则要将栈顶元素弹出,直到满足条件为止,然后将该元素入栈
作用:
- 复杂度是线性的
- 单调递增栈可以找到往左/右第一个比当前元素大的元素
- 单调递减栈可以找到往左/右第一个比当前元素小的元素
- 可以求得以当前元素为最值的最大区间
问题描述:
给一个直方图,求直方图中的最大矩形的面积。例如,下面这个图片中直方图的高度从左到右分别是2, 1, 4, 5, 1, 3, 3, 他们的宽都是1,其中最大的矩形是阴影部分。
input:
输入包含多组数据。每组数据用一个整数 来表示直方图中小矩形的个数 你可以假定 。 然后接下来 个整数 满足 。 这些数字表示直方图中从左到右每个小矩形的高度,每个小矩形的宽度为 。 测试数据以 结尾。
output:
对于每组测试数据输出一行一个整数表示答案。
样例输入:
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
样例输出:
8
4000
解题思路:
现在,每一个矩形的高都是已知的,但是矩阵的宽还不清楚。如果要求最大的矩形面积,就要已知矩形的高和宽,则在矩形高已知的情况下,要使矩形面积最大,肯定宽越大,即左端点越靠左,右端点越靠右,矩形的面积才可能最大。那左端点可以确定为往左数第一个小于此高度的点,右端点可以确定为往右数第一个小于此高度的点。
利用这个特点,我们可以使用两次单调递减栈,第一次栈找到第一个小于此高度的右端点,第二次栈找到第一个小于此高度的左端点。
接下来我们考虑实现,这里我们并没有使用stl中的stack,而是自己手写模拟栈,我认为这样更方便些。栈用一个数组表示,用一个top表示栈顶指针,top始终指向栈顶的位置。栈中的每一个元素都是一个结构体类型,有四个域,存储它的值,它的数组下标,它的左端点和右端点的值。因为单调栈的复杂度是线性的,我们使用一次for循环就可以完成对一个端点的查找。在求一个端点时,首先考虑要被放入到栈的元素是否小于栈顶元素,若小于,则栈顶元素弹出,并且修改栈顶元素的右端点的值为被放入栈的元素的下标,重复以上步骤,直到大于为止,在一次for循环结束后,栈中剩余元素的右端点就是最又端了。对于求左端点,只不过是将数组逆序再调用一次递归栈,原理类似。
解题总结:
如果该题目能够想到使用单调栈就十分容易了,但是要注意一点,如果变量类型使用int的话会爆精度,所以一定要使用long long!!
代码:
#include<iostream>
using namespace std;
struct limit{
long long int low; //左端点
long long int high; //右端点
long long int element;
long long int location; //数组下标
limit & operator = (const limit &l)
{
low=l.low;
high=l.high;
element=l.element;
location=l.location;
return *this;
}
limit()
{
low=0;
high=0;
element=0;
location=0;
}
};
int main()
{
long long int n;
cin>>n;
while(n)
{
limit *p=new limit [n+1];
for(int i=1;i<=n;i++)
{
long long int temp;
cin>>temp;
p[i].element=temp;
p[i].location=i;
}
long long int top=0;
limit *q=new limit [n+1];
for(int i=1;i<=n;i++) //找到右端点
{
while(top!=0&&(q[top].element>p[i].element))
{
long long int lo=q[top].location;
p[lo].high=i;
top--;
}
top++;
q[top]=p[i];
}
while(top!=0)
{
long long int lo=q[top].location;
p[lo].high=n+1;
top--;
}
limit *qq=new limit [n+1];
for(int i=n;i>=1;i--) //找到左端点
{
while(top!=0&&(qq[top].element>p[i].element))
{
long long int lo=qq[top].location;
p[lo].low=i;
top--;
}
top++;
qq[top]=p[i];
}
while(top!=0)
{
long long int lo=qq[top].location;
p[lo].low=0;
top--;
}
long long int maxn=0;
for(int i=1;i<=n;i++) //求出以每一个矩形为高的最大面积
{
long long int h=p[i].high;
long long int l=p[i].low;
long long int s=p[i].element*(h-l-1);
if(s>maxn)
maxn=s;
}
cout<<maxn<<endl;
delete [] p;
delete [] q;
delete [] qq;
cin>>n;
}
}