poj 2559(单调栈)

poj 2559(单调栈)

对于此题,我们需要找到每个点分别向左右能够扩展到的连续长度,能够扩展的条件为:这些区间的元素值都都大于等于当前点的值。
使用单调栈维护从起点到当前点的递增序列,如果栈顶元素大于当前点,我们就一直将栈中元素弹出,直到遇到小于当前点的元素,以此来维护栈的递增性,显然此时的点最远可以扩展到当前栈元素的位置-1,即我们找到了当前点的扩展边界。我们需要证明的是:前面弹出的元素对之后的点没有影响,即i之前大于h[i]的元素必然不是h[i+1]可以扩展到的边界。我们可以分情况讨论

  1. 若h[i+1]>h[i] 那么 h[i+1]显然无法再向左扩展,边界是h[i],证明是对的
  2. 若h[i+1]<=h[i] 那么 之前\({j<i , h[j]>h[i]}\)显然都是满足扩展条件的,那么这些点都不能成为边界,即因为h[i]而弹出的元素对h[i+1]是没有影响的。证明是对的
    这样的证明是具有递推性的,我们可以用数学归纳法证明其正确性。

由此我们可以线性求得每个元素向左扩展的边界,向右扩展的边界只需倒着做一遍相同的操作即可,总复杂度是O(n)。

#include <queue>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <vector>
#define ll long long
#define inf 1000000000LL
#define mod 1000000007
using namespace std;
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
const int N=1e5+10;
int L[N],R[N],st[N],h[N];
int main(){
     while(true){
        int n=read();
        if(n==0) break;
        for(int i=0;i<n;i++) h[i]=read();
        int t=0;
        for(int i=0;i<n;i++){
             while(t>0&&h[st[t-1]]>=h[i]) t--;
             L[i]=t==0?-1:st[t-1];
             st[t++]=i;
        }

        t=0;
        for(int i=n-1;i>=0;i--){
             while(t>0&&h[st[t-1]]>=h[i]) t--;
             R[i]=t==0?n:st[t-1];
             st[t++]=i;
        }

        ll ans=0;
        for(int i=0;i<n;i++){
            ans=max(ans,(ll)h[i]*(R[i]-L[i]-1));
        }
        printf("%lld\n",ans);
     }
     return 0;
}
posted @ 2017-05-05 14:37  江南何采莲  阅读(1193)  评论(2编辑  收藏  举报