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; }