一道单调栈基础题,也可以用双端队列来做
题目链接:http://cstest.scu.edu.cn/soj/problem.action?id=3085
题意:
在一个非负数序列中寻找一个“价值”最大的子序列,
价值的定义是:该子序列的最小值*子序列和
算法:
O(n)算法:
需遍历序列两次。
依次算出以元素a[i]为最小值的序列的最大”价值”。。。(这样说是不是有点儿绕)
好吧。。换个说法。。
针对每个a[i],求出l[i],r[i],使得区间(r[i],l[i])尽量大,且区间(r[i],l[i])上的数均不大于a[i]
显然l[i] 就是i左边第一个比它小的数的下标,显然r[i] 就是i右边第一个比它小的数的下标,
在求l[i]和r[i]的时候需要用两个单调栈来求:
以求l[i]为例
首先入栈一个0,并令a[0]=-1,这样可以防止出现空栈
每个下标入栈的时候,将栈顶中所有代表元素比它大的下标弹出,
此时的栈顶下标即是l[i],
然后再将i入栈
最后求a[i]*(sum[r[i]-1]-sum[l[i]])的最大值
#include<cstdio> #include<stack> #define INF 0x3f3f3f3f using namespace std; int a[100010],l[100010],r[100010]; long long sum[100010]; int main() { int n,i; long long ans; while(~scanf("%d",&n)) { ans=sum[0]=0; a[0]=a[n+1]=-INF; stack<int>ss; ss.push(0); for(i=1;i<=n;i++) { scanf("%d",&a[i]); sum[i]=sum[i-1]+a[i]; while(a[ss.top()]>=a[i])ss.pop(); l[i]=ss.top(); ss.push(i); } while(!ss.empty())ss.pop(); ss.push(n+1); for(i=n;i>=1;i--) { while(a[ss.top()]>=a[i])ss.pop(); r[i]=ss.top(); ss.push(i); ans=ans>a[i]*(sum[r[i]-1]-sum[l[i]])?ans:a[i]*(sum[r[i]-1]-sum[l[i]]); } printf("%lld\n",ans); } }