浅谈栈:https://www.cnblogs.com/AKMer/p/10278222.html
题目传送门:https://lydsy.com/JudgeOnline/problem.php?id=1345
假设当前序列是单调的,那么显然答案就是从低往高合并的权值和。
假设(aleqslant b leqslant c),那么合并(a,b)然后合并(b,c)的代价是(b+c),显然要比合并(b,c)再合并(a,c)的代价(c+c)要优。
如果当前序列不单调,那么必然存在一个位置(pos)满足(a[pos-1]geqslant a[pos] leqslant a[pos+1])。我们只需要把(a[pos])与(a[pos-1],a[pos+1])中值较小的那个合并即可。可以证明,能与(a[pos])合并的最小值就是(min(a[pos-1],a[pos+1])),如果(a[pos-1])或者(a[pos+1])之前与其它数合并过,并且发生了改变,那么改变之后的数值显然是大于(min(a[pos-1],a[pos+1]))的。所以我们只需要维护一个单调栈,发现这种情况判断更新答案即可。最后会剩下一个单调递减的序列,我们再按照第一种情况处理就行。
时间复杂度:(O(n))
空间复杂度:(O(n))
代码如下:
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e6+5,inf=2e9+5;
ll ans;
int n,top;
int stk[maxn];
int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
}
int main() {
n=read();
stk[0]=inf;
for(int i=1;i<=n;i++) {
int x=read();
while(top&&stk[top]<=x)
ans+=min(stk[top-1],x),top--;
stk[++top]=x;
}
top--;
while(top)ans+=stk[top],top--;
printf("%lld
",ans);
return 0;
}