Processing math: 100%

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 @   江南何采莲  阅读(1199)  评论(2编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示