题意
大小为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个数就一定是可以的,因为是递增。
然后只需要把t到i中间这些数降低成为a[i]就可以(a[n]是楼的最高层数)
转移方程就是 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;
}