题意:给一序列,可对这个序列的每个数做加一或减一的操作,问最小的操作数使这个序列变为严格递增序列
首先如果题目要求的序列是非严格递增序列的话,则产生的新序列所含有的数一定可以都是原序列的数。
大致的证明思路:如果一个数要变小,那么这个数只要小到和它右边的数一样大就好了,再小得到的解不会是最优解;如果一个数要变大,那么这个数只要大到和它左边的数一样小就好了,再大得到的解不会是最优解。
试着把原问题转换为非严格递增序列的问题:只要使a[i]-=i就好了
转化为非严格递增后,如何解决,既然每个数都是原序列中的数,用dp[i][j]表示第i个数变为原序列第j大的数需要的成本,则有dp[i][j]=abs(b[i]-a[j])+min(dp[i-1][k]),b[i]指第i个数,a[j]指序列中第j大的数
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAXN=3000+5;
int a[MAXN],b[MAXN];
LL maxn[MAXN],dp[MAXN][MAXN];
int main(){
int n;
while(~scanf("%d",&n)){
for(int i=0;i<n;++i){
scanf("%d",a+i);
a[i]-=i;
b[i]=a[i];
}
sort(a,a+n);
int sub=unique(a,a+n)-a;
for(int i=0;i<sub;++i){
dp[0][i]=abs(b[0]-a[i]);
if(i==0) maxn[i]=dp[0][i];
else maxn[i]=min(dp[0][i],maxn[i-1]);
}
for(int i=1;i<n;++i){
for(int j=0;j<sub;++j){
dp[i][j]=abs(b[i]-a[j])+maxn[j];
if(j==0) maxn[j]=dp[i][j];
else maxn[j]=min(maxn[j-1],dp[i][j]);
}
}
LL ans=1e18;
for(int i=0;i<sub;++i)
ans=min(ans,dp[n-1][i]);
printf("%I64d\n",ans);
}
}

本文解析了CodeForces上的一道题目E,通过转换问题为非严格递增序列求解,采用动态规划方法,详细介绍了算法实现过程及代码。
266

被折叠的 条评论
为什么被折叠?



