单调栈(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;
}
View Code

posted on 2019-03-14 12:51  小叶子曰  阅读(273)  评论(0编辑  收藏  举报

导航