题意:连续输入n个紧挨着的宽度为1的矩形高度,求组成矩形的最大面积。
分析:
这题是个好题,迄今为止我已经见到过四种解法了,最容易想到的有dp与栈操作,此外还有单调队列和斜率优化的方式,综合时间空间与代码实现难度而言,这里我推荐dp大法。
此题最关键的地方便是能否想到用边界来划分区域,只要我们确保第i个矩形的左边界与右边界之间的矩形都高于第i个矩形,那么边界内最大面积必为height[i] * (right[i] - left[i] + 1)。而后比较每一个矩形的边界内最大面积即可。
难的地方在于如何得到每一个矩形的左右边界,我们可以先将每一个矩形的左右边界设为自身,并得到这样两个限制条件:
1.第i个矩形左边界(包括左边界)所在矩形必高于第i个矩形,第i个矩形的左边界所在矩形的左边界同时也是第i个矩形的左边界;
2.左边界大于1;
通过这两个条件,我们可以将左边界一直向左递推出去,便得到:
while(left[i] > 1 && height[left[i] - 1] >= height[i])
left[i] = left[left[i] - 1];
右边界同理。
代码:
//dp方法
/*
* @author Novicer
* language : C++/C
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
lint h[100005],lf[100005],rt[100005];
int main(){
lint n;
while(scanf("%I64d",&n) == 1){
if(n == 0) break;
for(int i = 1 ; i <= n ; i++){
scanf("%I64d",&h[i]);
lf[i] = i;
rt[i] = i;
lint tmp = i;
while(h[lf[tmp] - 1] >= h[tmp] && lf[tmp] > 1)
lf[tmp] = lf[lf[tmp] - 1];
}
for(int i = n ; i >= 1 ; i--){
lint tmp = i;
while(h[rt[tmp] + 1] >= h[tmp] && rt[tmp] < n)
rt[tmp] = rt[rt[tmp] + 1];
}
lint ans = 0;
for(int i = 1 ; i <= n ; i++){
ans = max(ans , h[i] * (rt[i] - lf[i] + 1));
}
cout << ans << endl;
}
return 0;
}
//单调栈方法
#include <stdio.h>
#include <iostream>
using namespace std;
#define max(a,b) a > b ? a : b
#define N 100005
int q[N]={-1},w[N]; //w记录的是从这个点开始,之前有几个高度大于等于此高度.
int main()
{
int n,h;
while(scanf("%d",&n),n)
{
int top = 0;
long long ans = 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 , w[top] = 1;
else
{
long long cnt = 0;
while(h <= q[top])
{
ans = max(ans ,(cnt+w[top])*q[top] );
cnt += w[top--];
}
q[++top] = h;
w[top] = cnt+1;
}
}
cout << ans << endl;
}
return 0;
}