一道单调栈基础题,也可以用双端队列来做
题目链接: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);
}
}
PS:这篇题解写的太渣了。。估计不会单调栈的人看了我这篇题解还是不会。。得是会单调栈的人才看得懂。。