欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Codeforces-1313C2-Skyscrapers (hard version)(单调栈)

程序员文章站 2022-06-04 17:46:20
...

题目链接

题意

大小为n的数组表示摩天大楼的最大层数,要一个单峰的序列,求n栋楼的层数总和的最大值。

思路

easy版本可以用n2的方法,选取顶峰,然后求层数。

用单调栈可以O(n)解决。

单调栈基本思路讲解

大概就是,用单调栈,就可以用O(n)的复杂度解决,数组向左(右)遍历,第一个比它小(大)的值。所以给一个数组a[n],就可以得到比a[i]小,在 i 的左边且位置离 i 最近的a[j],可以把这个位置存到一个数组中。

用O(n)得到这个数组之后,对这个题来看,用dp的思想,得到一个前缀 l [n]的东西,表示以 i 为顶峰,左边的最大层数是多少。

求 l [i] ,找到比 a[i] 小的第一个数,位置是t,那么t前面这t个数就一定是可以的,因为是递增。

Codeforces-1313C2-Skyscrapers (hard version)(单调栈)

然后只需要把t到i中间这些数降低成为a[i]就可以(a[n]是楼的最高层数)

Codeforces-1313C2-Skyscrapers (hard version)(单调栈)

转移方程就是 l[i] = l[t] + (i-t) * a[i] 

这只是求出来了峰值左边的最大,右边同理。然后走一遍求一下峰值在哪里总和最大就可以了。

代码参考


#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f

using namespace std;

const int N = 5e5+10;

ll l[N],r[N],st[N],k;
int n,a[N];

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);

    k = 0;
    for(int i=1;i<=n;i++){
        while(k!=0&&a[st[k]]>a[i]) k--;
        int t;
        if(k==0) t = 0;
        else t = st[k];
        l[i] = l[t] + 1ll*a[i]*(i-t);
        st[++k] = i;
    }

    k = 0;
    for(int i=n;i>=1;i--){
        while(k!=0&&a[st[k]]>a[i]) k--;
        int t;
        if(k==0) t = n+1;
        else t = st[k];
        r[i] = r[t] + 1ll*a[i]*(t-i);
        st[++k] = i;
    }

    ll mmax = 0; int p;
    for(int i=1;i<=n;i++){
        ll t = l[i] + r[i] - a[i];
        if(t>mmax){
            mmax = t;
            p = i;
        }
    }

    for(int i=p-1;i>=1;i--) a[i] = min(a[i],a[i+1]);
    for(int i=p+1;i<=n;i++) a[i] = min(a[i],a[i-1]);

    for(int i=1;i<=n;i++) printf("%d ",a[i]);

    return 0;
}

 

相关标签: 单调栈