题意:每个数可以与前一个或者后一个点合并,使得数列被合并成单调不降的序列,求出最少的操作数。
思路:考虑dp
首先有一个很贪的思路,在相同的合并操作数下,需要合并出来的数越小越好,这样后面的数就能以更小的代价跟它匹配。另外,如果一个数满足不降的条件,那么就没必要把它合并掉,这样显然不优。
考虑正难则反。即是把题意转化成求最大划分连续序列数。表示前i个点能划分出来的最大次数,而
表示以i结尾的上一个段操作数为j的合并出来的最小数,有两种策略转移:
1.当前数与上一个数合并。
2.当前数与第j个数合并,使得被分成一块。
最后答案为,复杂度
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5010;
int n,a[N],f[N],s[N],g[N][N];
void solve(){
memset(g,0x3f,sizeof g);
f[1]=1;g[1][1]=a[1];//以i为结尾而且分成了j段的最小值
for(int i=2;i<=n;i++){
f[i]=f[i-1];g[i][f[i]]=g[i-1][f[i-1]]+a[i];
for(int j=i-1;j>=1;j--){
//j+1 ~ i 分成一段
int lst=g[j][f[j]],h=s[i]-s[j];
if(h>=lst){
if(f[j]+1>=f[i]){
f[i]=f[j]+1;
if(g[i][f[i]]>h) g[i][f[i]]=h;
}
}
}
}
cout<<min(n-f[n],n-1);//
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
s[i]=s[i-1]+a[i];
}
solve();
return 0;
}