题目大意: 一个横轴等距且为1的直方图,直方图总长为n,现给出每一列上的纵坐标hi(0 <= hi <= 1000000000)。求出直方图包含的面积最大的矩形的面积。
解题思路:枚举起点i,找以高为height[i]的最大矩形,假设右边界最远到达di,那么ans=max{(di+1-i)*height[i] | i=1,2,3……n} (**)。当然不能裸着去暴力找每个di,那样的效率接近n^2。不难发现一些隐藏条件(i<j):
- 如果height[j]<height[i],那么di < j。这就意味着两个信息:
- 如果j是从i递增第一个搜索到的值,那么di=j
- (dj+1-j)*height[j]肯定也不是最优解,因为(dj+1-i)*height[j](从i开始以height[j]为高找矩形,貌似这个式子不在(**)中,但是如果更新height[i]=height[j]就不一定了)比它更优。
- 如果当前答案ans >= (n+1-i)*height[i],不管di是多少,它肯定不是最优解
这样思路是不是就很好想了,用一个队列记录所有当前可选的i,又由于队列中的i,j没有height[j] < height[i] (i < j),故que中记录单调非递减的可能解。这个就叫单调队列。
看代码:(初学者可以试试自己做样例,看看注解代码里的输出信息)
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 using namespace std; 6 #define f(head,i) height[que[head]]*(i-que[head]) //矩形[que[head],i-1]的面积 7 typedef long long lli; 8 lli height[1001000]; 9 int que[10010000]; 10 int main() 11 { 12 int n,head,tail; 13 while(scanf("%d",&n) && n > 0){ 14 for(int i=1;i<=n;i++) 15 scanf("%lld",&height[i]); 16 lli ans=0; 17 head=tail=0; 18 que[tail++]=1; 19 int n1=n+1; height[n1]=0; 20 for(int i=2;i<=n1;i++){ 21 while(head+1<tail && f(head,n1)<=ans) //队列中至少有一个值,避免了队列为空时的特判 22 head++; 23 ans = max(ans,f(head,i)); 24 /* 25 printf(" %d ans = %lld ",i,ans); 26 cout<<"que : "; for(int j=head;j<tail;j++) cout<<que[j]<<" "; cout<<endl; 27 cout<<"hei : "; for(int j=head;j<tail;j++) cout<<height[que[j]]<<" "; cout<<endl; 28 */ 29 while(head+1<tail && height[i]<=height[que[tail-2]]) 30 tail--, ans = max(ans,f(tail,i)); 31 if(height[i]<=height[que[tail-1]]) 32 ans = max(ans,f(tail-1,i)),height[que[tail-1]]=height[i]; 33 else 34 que[tail++]=i; 35 /* 36 printf("%d ans = %lld ",i,ans); 37 cout<<"que : "; for(int j=head;j<tail;j++) cout<<que[j]<<" "; cout<<endl; 38 cout<<"hei : "; for(int j=head;j<tail;j++) cout<<height[que[j]]<<" "; cout<<endl; 39 */ 40 } 41 cout<<ans<<endl; 42 } 43 return 0; 44 }