题意:长度为n的序列a,定义位置p为合法,当 a[p]>a[p-1] && a[p]>a[p+1].
操作:选择某个pos,令a[pos]--.
n<=5e3,a[i]<=1e5. 对k=[1...n/2] 询问,至少k个位置合法时的最小操作次数?
先分析一下: 位置p合法 则位置 p-1, p+1都为非法.
若a[p-1]>p 或者 a[p+1] > a[p] 为了让a[p]合法,最少要把a[p-1],a[p+1]变为a[p]-1.
求最优解,无法贪心时,开始考虑DP...
设dp[i][k] 为前n个数中,总共有k个合法位置,并且位置i为最后一个合法位置时的最小操作次数.
dp[i][k] = min( min(dp[1,2...i-3][k-1])+fun(a[i],a[i-1] , dp[i-2][k-1] +fun(min(a[i-1],a[i-2]-1),a[i]) ) + fun(a[i+1[,a[i]) .
res[k]= min(res[k+1],dp[1:n][k]).
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e3+5,M=2600,inf=0x3f3f3f3f;
int n,a[N],dp[N][M],res[N];
int fun(int x,int y){
// to make x > y;
int op=y-x+1;
if(op<0) op=0;
return op;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
memset(dp,inf,sizeof(dp));
memset(res,inf,sizeof(res));
int m=(n+1)/2;
for(int i=1;i<=n;i++) dp[i][1]=fun(a[i],a[i-1])+fun(a[i],a[i+1]),res[1]=min(res[1],dp[i][1]);
for(int k=2;k<=m;k++){
int mn=inf;
for(int i=2;i<=n;i++){
if(i-3>=1) mn=min(mn,dp[i-3][k-1]);
int v1=mn+fun(a[i],a[i-1])+fun(a[i],a[i+1]);
int v2=dp[i-2][k-1]+fun(a[i],min(a[i-1],a[i-2]-1))+fun(a[i],a[i+1]);
dp[i][k]=min(v1,v2);
res[k]=min(res[k],dp[i][k]);
// cout<<i<<' '<<k<<' '<<v1<<' '<<v2<<' '<<dp[i][k]<<'\n';
}
}
for(int k=m-1;k>=1;k--) res[k]=min(res[k],res[k+1]);
for(int i=1;i<=m;i++) cout<<res[i]<<' ';
return 0;
}