【CF442C】Artem and Array
题目
题目链接:https://codeforces.com/problemset/problem/442/C
给定长度为 \(n\) 的数组 \(a\) ,你需要进行 \(n\) 次操作:删去某一元素 \(a_i\) ,并获得 \(\min\{a_{i-1}, a_{i+1}\}\) 的分数。若不存在 \(a_{i-1}\) 或 \(a_{i+1}\),则此次操作不得分。
请你计算至多能得到多少分。
思路:
首先任意时刻如果存在连续三个数 \(x,y,z\) 满足 \(y\leq x\) 且 \(y\leq z\),那么此时删去 \(y\) 一定最优。因为如果此时不删去 \(y\),后面某次操作删去了 \(x\) 或 \(z\) 中的一个,那么得分将不大于 \(y\),显然没有删去 \(y\),得分为 \(\min(x,z)\) 优秀。
所以我们先删去所有比他两边小的元素。注意删去一个元素 \(a_i\) 之后还要判断 \(a_{i+1}\) 是否能删去 \(a_{i-1}\),所以可以用一个“单调”栈维护。这个单调栈维护一个类似山峰状的元素,加入一个数 \(x\) 后就一直弹出栈顶直到栈顶大于栈顶第二个元素或即将加入的这个数。
对于剩余的数,我们显然无法取到最大值和次大值,并且我们最后一定只会留下两个数,所以不是最大值和次大值的数一定都会被取。直接扫一遍计算答案即可。
时间复杂度 \(O(n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=500010;
int n,top,max1,max2,st[N];
ll ans;
int main()
{
scanf("%d",&n);
for (int i=1,x;i<=n;i++)
{
scanf("%d",&x);
while (top>=2 && st[top]<=st[top-1] && st[top]<=x)
top--,ans+=min(st[top],x);
st[++top]=x;
}
for (int i=1;i<=top;i++)
{
ans+=st[i];
if (st[i]>max1) max2=max1,max1=st[i];
else if (st[i]>max2) max2=st[i];
}
printf("%lld",ans-max1-max2);
return 0;
}