POJ - 2796 Feel Good (单调栈)

题目传送门:POJ - 2796 Feel Good 

题目大意:

给你一组个数组,需要你找到一段子区间的和乘上该区间的最小值是最大值。输出结果和区间

分析:

首先区间上的和可以使用前缀和,这样可以O(1)找到每个区间的和。现在需要考虑的便是每个区间

对应的最小值,如果暴力则存在n2个区间,每个区间在找到最小值一定一定会超时。可以想到数组

区间的最小值无非是数组原来的数,对于这n个数,可以看他向左右延伸的宽度,即在这个区间内

一定是以该值为最小值。因此需要找到在该数左边部分第一个比他小的,在右边第一个比他小的,在

这段区间内最小值均为该数。很显然可以维护一个单调递增的栈就可以求出结果。然后枚举n个最小

值和对应区间和的乘积,取最大即可。

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<stack>
using namespace std;
const int MAX=100009;
long long a[MAX];
long long sum[MAX];
int n,l[MAX],r[MAX];
stack<int>st;
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        sum[0]=0;
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
        for(int i=1;i<=n;i++)
        {
            while(!st.empty()&&a[st.top()] >= a[i])     
            {
                r[st.top()] = i-1;//当前插入a[i]的数值比栈顶元素小,因此栈顶元素向右只能扩展到
                                 //i-1这个位置,因为过了i,最小值就变为a[i]了,所以记录栈顶元素
                                 //向右扩展的边界 
                st.pop();
            }
            
            if(st.empty())l[i]=1;//因为是单调递增栈,因此新插入元素a[i]左端元素是比插入的元素小的 
            else l[i]=st.top()+1;//因此他向左扩展的值就是st.top()+1 
            st.push(i);
        }
        while(!st.empty())        //将剩余的元素一次弹出,并且每个元素向右都能扩展至最后一个元素 
        {
            r[st.top()]=n;
            st.pop();
        }
        long long ans=0;
        int id=-1;
        for(int i=1;i<=n;i++)//枚举求结果 
        {
            if(ans<=(sum[r[i]]-sum[l[i]-1])*a[i])
            {
                ans=(sum[r[i]]-sum[l[i]-1])*a[i];
                id=i;
            }
        }
        printf("%I64d\n",ans);
        printf("%d %d\n",l[id],r[id]);
    }

    return 0;
}

 

posted @ 2019-07-30 18:04  _Carrot  阅读(349)  评论(0编辑  收藏  举报