题目:hdu1506
题意:一个柱形图,宽均为1.内部最大的矩形的面积。
解答1:刚开始想的是从某个区间内找小的高遍历所有区间找出最大的矩形。但是复杂度太高啦!
正确的解答:
从每一条出发,分别求出这一条左边连续比它高的和右边连续比它高的坐标。然后遍历一遍便可以求出最大值。
左右!!!可以分别从左边和右边遍历求!!!
以左边为例:
如果左边那个比它高,那么比它左边还高的一定比它还高!
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const int MAXN = 100000 + 10;
long long int a[MAXN],r[MAXN],l[MAXN];
int main()
{
long long int n;
while(~scanf("%lld",&n) && n)
{
for(int i = 1;i <= n;i++)
scanf("%lld",&a[i]);
memset(l,0,sizeof(l));
memset(r,0,sizeof(r));
l[1] = 1;
r[n] = n;
for(int i = 2;i <= n;i++)
{
int tmp = i;
while(tmp > 1 && a[tmp-1] >= a[i])
tmp = l[tmp-1];
l[i] = tmp;
}
for(int i = n-1;i >= 1;i--)
{
int tmp = i;
while(tmp < n && a[tmp+1] >= a[i])
tmp = r[tmp+1];
r[i] = tmp;
}
long long int ans = 0;
for(int i = 1;i <= n;i++)
{
ans = max(ans,(long long int)((r[i]-l[i]+1)*a[i]));
}
printf("%lld\n",ans);
}
return 0;
}
解答2:从左、从右各维护一个单调栈。
单调栈:栈里面的元素是单调的(从小到大或者从大到小)
1、如果栈里元素从小到大:可以直接找到从该位置往左,第一个比它小的元素。
2、如果栈里元素从大到小:可以直接找到从该位置往右,第一个比它大的元素。
这个题,要找到一个位置往左、往右,第一个比它小的元素(如果比它大就可以扩展)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int MAXN = 100005;
long long int a[MAXN],l[MAXN],r[MAXN];
struct g
{
long long int h,n;
};
g q1[MAXN],q2[MAXN];
int main()
{
int N;
while(~scanf("%d",&N) && N)
{
for(int i = 1;i <= N;i++)
scanf("%d",&a[i]);
long long int ans = 0;
int top = 0;
q1[0].h = 0;
q1[0].n = 0;
q1[++top].h = a[1];
q1[top].n = 1;
l[1] = 1;
for(int i = 2;i <= N;i++)
{
if(a[i] > q1[top].h)
{
q1[++top].h = a[i];
q1[top].n = i;
l[i] = i;
}
else
{
while(q1[top].h >= a[i] && top >= 1)
top--;
q1[++top].h = a[i];
q1[top].n = i;
l[i] = q1[top-1].n + 1;
}
}
top = 0;
q1[0].h = 0;
q1[0].n = N+1;
q1[++top].h = a[N];
q1[top].n = N;
r[N] = N;
for(int i = N-1;i >= 1;i--)
{
if(a[i] > q1[top].h)
{
q1[++top].h = a[i];
q1[top].n = i;
r[i] = i;
}
else
{
while(q1[top].h >= a[i] && top >= 1)
top--;
q1[++top].n = i;
q1[top].h = a[i];
r[i] = q1[top-1].n - 1;
}
}
for(int i = 1;i <= N;i++)
ans = max(ans,(long long int)((r[i]-l[i]+1)*a[i]));
printf("%lld\n",ans);
}
return 0;
}
解答3:依然是单调栈。但是只扫一遍。
有以下结论:
1、如果可以直接入栈,那么它可以向左扩展的长度为1(它自己)。
2、第二个出栈的元素的右宽是前一个出栈的元素的总宽。
3、h的左宽是上一个出栈元素的总宽。
进栈的时候你只能知道它向左扩展的长度(记录下来),出栈的时候你可以算出它向右扩展的长度(出栈意味着它比当前高度要高,就说明它向右扩展不能扩展到当前元素了,那么这个向右扩展的长度是几呢?)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int main()
{
int n,h;
int q[100005],l[100005];
while(~scanf("%d",&n)&&n)
{
long long int ans = 0;
memset(q,-1,sizeof(q));
memset(l,0,sizeof(l));
int top = 0;
for(int i = 1;i <= n+1;i++)
{
if(i!=n+1)
scanf("%d",&h);
else
h = 0;//最后把所有的元素弹出栈
if(h > q[top])
q[++top] = h,l[top] = 1;//直接进,向左扩展为1(自己)
else//凡是出栈的元素的高度都比h高(向右扩展不到当前的元素)
{
long long int r = 0;
while(h <= q[top])
{
ans = max(ans,q[top]*(l[top]+r));
r += l[top--];
}
q[++top] = h;
l[top] = r + 1;//加上自己
}
}
printf("%lld\n",ans);
}
return 0;
}