题意
有一序列 (a),每一步你可以做以下两种操作之一:
- 将 (a[l..r]) 减一;
- 将 (a[i]) 减 (x);
- (你需要保证操作后数字非负)
问最少多少步将整个序列变成 (0)。(nleq 5 imes 10^3)(实际可以做到 (O(nlog n)) 甚至 (O(n)))。
题解
不会出现左图的情况,左图一定能化为右图且答案不会更劣。
故递归处理:对于一段,找到其最小值,这一段有两种策略:
- 这一段的最小值部分用第一种操作,接着序列被 (0) 分成多个部分,每个部分递归找最优解;
- 这一段全部用第二种操作。
暴力是 (O(n^2)) 的,用笛卡尔树容易做到 (O(n))(没写)。
#include<bits/stdc++.h>
using namespace std;
int getint(){
int ans=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ans=ans*10+c-'0';
c=getchar();
}
return ans*f;
}
const int N=5e3+10;
#define ll long long
int a[N];
ll solve(int l,int r){
int mn=0x7f7f7f7f;
for(int i=l;i<=r;i++)mn=min(mn,a[i]);
ll ans=0;
int t=l;
for(int i=l;i<=r;i++){
a[i]-=mn;
if(a[i]==0){
if(i!=t)ans+=solve(t,i-1);
t=i+1;
}
}
if(t!=r+1)ans+=solve(t,r);
ans+=mn;
ans=min(ans,r-l+1ll);
// cerr<<"> "<<l<<" "<<r<<" "<<ans<<endl;
return ans;
}
int main(){
int n=getint();
for(int i=1;i<=n;i++)a[i]=getint();
cout<<solve(1,n);
}