单调栈(SOJ 3085)
SOJ 3085: windy's cake V http://acm.scu.edu.cn/soj/problem.action?id=3085
问题:给出$n$个正整数$num[1], ..., num[n]$, 定义从$i$到$j$的子列的分数为
$(\min_{i\le k\le j}num[k])*\left(\Sigma_{k=i}^{j}num[k]\right)$.
求解具有最大分数的子列并输出最大分数。
分析:我们可以枚举每个$num[i], 1\le i\le n$在一个子列中为最小值。所以,对每个$num[i], 1\le i\le n$,我们需要找出它对应的子列,即是从$i-1$到$1$中找出第一个比$num[i]$小的数$num[i']$以及从$i+1$到$n$中找出第一个比$num[i]$小的数$num[i'']$(类似于SOJ 2511,参考这里)。然后$num[i]$的分数为
$score[i]=num[i]*\left(\Sigma_{k=i'}^{i''}num[k]\right)$,
最大分数为$\max_{1\le k\le n}score[i]$.
方法:维护一个单调递增栈。
代码:
#include<iostream>
#include<stack>
using namespace std;
struct node
{
int no;
int num;
int prevNo;
node(){prevNo=0;}
};
node arr[100005];
stack<node>s;
long long sum[100005];
int main()
{
int n;
int temp1;
int i;
long long temp2;
long long ans;
while(scanf("%d",&n)==1)
{
sum[0]=0;
for(i=1;i<=n;i++)
{
scanf("%d",&temp1);
sum[i]=sum[i-1]+temp1;
arr[i].no=i;
arr[i].num=temp1;
}
arr[0].no=0;
arr[0].num=-1;
arr[n+1].no=n+1;
arr[n+1].num=-1;
s.push(arr[0]);
ans=0;
for(i=1;i<=n+1;i++)
{
while(arr[i].num<s.top().num)
{
temp2=s.top().num*(sum[i-1]-sum[s.top().prevNo]);
ans=temp2>ans ? temp2 : ans;
s.pop();
}
arr[i].prevNo=s.top().no;
s.push(arr[i]);
}
while(!s.empty())
s.pop();
printf("%lld\n",ans);
}
return 0;
}