Largest Rectangle in a Histogram
Time Limit : 2000/1000ms (Java/Other) Memory Limit : 131072/65536K (Java/Other)
Total Submission(s) : 123 Accepted Submission(s) : 25
![](https://i-blog.csdnimg.cn/blog_migrate/45660aba95464e61adfbbcc7cea2c44c.jpeg)
Usually, histograms are used to represent discrete distributions, e.g., the frequencies of characters in texts. Note that the order of the rectangles, i.e., their heights, is important. Calculate the area of the largest rectangle in a histogram that is aligned at the common base line, too. The figure on the right shows the largest aligned rectangle for the depicted histogram.
7 2 1 4 5 1 3 3 4 1000 1000 1000 1000 0
8 4000
/*
给定从左到右多个矩形,已知这此矩形的宽度都为1,长度不完全相等。
这些矩形相连排成一排,求在这些矩形包括的范围内能得到的面积最大的矩形,打印出该面积。所求矩形可以横跨多个矩形,但不能超出原有矩形所确定的范围。
l[i]表示大于等于h[i]的最左边的位置,r[i]表示大于等于h[i]的最右边的位置,这样可以预处理出l[],r[],然后ans = max(ans, r[i] - l[i] + 1) * h[i])( 1 <= i <= n).*/
TLE:
#include <stdio.h>
#include<math.h>
#include<iostream>
using namespace std;
const int MAXN = 100010;
int l[MAXN], r[MAXN], h[MAXN];
int main()
{ int n;
while(~scanf("%d",&n),n)
{ int i;
for(i=1; i<=n; i++)
{ scanf("%d", &h[i]);
l[i]=r[i]=i;}
for(i=2; i<=n; ++i)
while (l[i] > 1 && h[i] <= h[l[i] - 1])
l[i] = l[i] - 1;
for(i=n-1; i>=1; --i)
while (r[i] < n && h[i] <= h[r[i] + 1])
r[i] = r[i] + 1;
__int64 ans=0;
for(i=1; i<=n; ++i)
ans=max(ans, (__int64)(r[i]-l[i]+1)*h[i]);
printf("%d\n",&ans);
}
}
如果用单调队列的话,复杂度变为O(N),时间快很多:
AC代码:
【分析】
这道题目是要求每个建筑物向左向右能扩展到的最大宽度,即左右两边比它高的连续的宽度。显然暴力枚举O(n^2)的复杂度是不可行的(该方法TLE,程序如上)。
考虑构造一个单调非递减队列,从左至右,依次加入到队列中,肯定会有元素出队列,
设当前要插入的数为a,要出队列的数为b,必有b>=a,则b向右能到达的最远距离就是b-a。
注意在求解时,让0先入队列,这样保证每个数据都会出队列。同理,左极限也可求出
*/
#include<stdio.h>
#include<string.h>
#define MAXN 1000000
long long h[MAXN+5];//建筑物的高度
int n; //建筑物的数目
int mq[MAXN+5]; //单调队列,队内元素为建筑物高度的下标
int lef[MAXN+5]; //left[i]:在第i个建筑物左侧,不比它的高度小的建筑物数量(该建筑物与i相邻且连续)
int righ[MAXN+5]; //right[i]:在第i个建筑物右侧,不比它的高度小的建筑物数量(该建筑物与i相邻且连续)
long long maxArea;
void CalcLeft()
{
mq[0] = 0;
int front = 0, rear = 1;
int i;
for (i = 1; i <= n; i++)
{
while (front < rear && h[i] <= h[mq[rear-1]])
{
rear--;
}
lef[i] = i - mq[rear-1] - 1;
mq[rear++] = i;//等价于 mq[rear]=i;i+=1;
}
}
void CalcRight()
{
mq[0] = n + 1;
int front = 0, rear = 1;
int i;
for (i = n; i >= 1; i--)
{
while (front < rear && h[i] <= h[mq[rear-1]])
{
rear--;
}
righ[i] = mq[rear-1] - i - 1;
mq[rear++] = i;
}
}
void MaxRectArea()
{
maxArea = -1;
int i;
for (i = 1; i <= n; i++)
{
long long area = (lef[i] + righ[i] + 1) * h[i];
if (area > maxArea)
{
maxArea = area;
}
}
//return maxArea;
}
int main()
{
while (~scanf("%d",&n)&&n)
{
int i;
memset(h,0,sizeof(h));
memset(mq,0,sizeof(mq));
memset(lef,0,sizeof(lef));
memset(righ,0,sizeof(righ));
for (i = 1; i <= n; i++)
{
scanf("%d",&h[i]);
}
h[0] = h[n+1] = -1;
CalcLeft();
CalcRight();
MaxRectArea();
/*for (i = 1; i <= n; i++)
{
cout << lef[i] << ' ';
}
cout << endl;*/
/*for (i = 1; i <= n; i++)
{
cout << righ[i] << ' ';
}
cout << endl;
*/
printf("%lld\n",maxArea);
}
return 0;
}
单调队列虽然原理简单,但不是那么容易理解程序实现的过程,我比较笨,我是根据一个例子一步一步模拟,看出其实现过程的。