CodeForces - 713C(经典DP)
题意:给出一个长度为n的数列(n<=3000),每次操作可以把其中一个数增大1或者减小1,求最少操作次数使得数列严格单调递增。
思路:
1.首先如果是变成一个非递减的序列,该怎么做?我们很明显可以发现:最后这个非递减的序列一定只是原来序列排序之后的结果。假设dp[i][j]表示当前枚举到第i个数,让它变成第j大的数需要的最小花费,那么转移方程很显然就是:
dp[i][j]=min(dp[i-1][k]+abs(a[i]-b[j]),dp[i][j]);
复杂度O(n * n * n),考虑一下优化dp[i-1][k]只是上一层的最小值,我们转移的时候记录一下最小值就行。
2.怎么把严格递增序列变成非严格递增序列呢?
a[i]<a[i+1] —> a[i]<=a[i+1]-1 —>a[i]-i<=a[i+1]-(i+1);
很神奇
/*
▄███████▀▀▀▀▀▀███████▄
░▐████▀▒▒▒▒▒▒▒▒▒▒▀██████▄
░███▀▒▒▒ACCEPTED▒▒▒▒▀█████
░▐██▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒████▌
░▐█▌▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒████▌
░░█▒▄▀▀▀▀▀▄▒▒▄▀▀▀▀▀▄▒▐███▌
░░░▐░░░▄▄░░▌▐░░░▄▄░░▌▐███▌
░▄▀▌░░░▀▀░░▌▐░░░▀▀░░▌▒▀▒█▌
░▌▒▀▄░░░░▄▀▒▒▀▄░░░▄▀▒▒▄▀▒▌
░▀▄▐▒▀▀▀▀▒▒▒▒▒▒▀▀▀▒▒▒▒▒▒█
░░░▀▌▒▄██▄▄▄▄████▄▒▒▒▒█▀
░░░░▄██████████████▒▒▐▌
░░░▀███▀▀████▀█████▀▒▌
░░░░░▌▒▒▒▄▒▒▒▄▒▒▒▒▒▒▐
░░░░░▌▒▒▒▒▀▀▀▒▒▒▒▒▒▒▐
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e3+5;
const int M=2e5+5;
const ll INF=1e18;
ll n,dp[N][N],pre[N],a[N],b[N];
int main() {
cin>>n;
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
for(int i=1; i<=n; i++) {
a[i]=a[i]-i;
b[i]=a[i];
}
sort(b+1,b+n+1);
for(int i=0; i<=n; i++)pre[i]=INF;
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
if(i==1)dp[i][j]=abs(a[i]-b[j]);
else dp[i][j]=pre[j]+abs(a[i]-b[j]);
pre[j]=min(pre[j-1],dp[i][j]);
}
}
cout<<pre[n]<<endl;
}