原版题意:给定一个序列,每次操作给其中一个数$+1$或$-1$,问最少需要多少操作使得整个序列为不下降序列。
题解:不下降序列最后修改完成后的各个数一定是原序列中的某一个数。关于这个的理解看到网上的一个解释:原序列,从左到右扫过去,如果左边的大于右边的,要么左边的减掉使其等于右边的,要么右边的加上使其等于左边的。
$dp[i][j]$:前i个数以原序列排序后的第j个数为结尾需要的最少操作。
状态转移方程:$dp[i][j]=min(dp[i][j-1],dp[i-1][j]+abs(a[i]-b[j]))$。当前 $i j$ 状态要么从末尾为第j-1个数转移过来,要么从前i-1个数+第i个数与末尾为第j个数的差值转移过来。
题目链接:http://codeforces.com/problemset/problem/713/C
题目:
Sonya was unable to think of a story for this problem, so here comes the formal description.
You are given the array containing n positive integers. At one turn you can pick any element and increase or decrease it by 1. The goal is the make the array strictly increasing by making the minimum possible number of operations. You are allowed to change elements in any way, they can become negative or equal to 0.
The first line of the input contains a single integer n (1 ≤ n ≤ 3000) — the length of the array.
Next line contains n integer ai (1 ≤ ai ≤ 109).
Print the minimum number of operation required to make the array strictly increasing.
7
2 1 5 11 5 9 11
9
5
5 4 3 2 1
12
In the first sample, the array is going to look as follows:
2 3 5 6 7 9 11
|2 - 2| + |1 - 3| + |5 - 5| + |11 - 6| + |5 - 7| + |9 - 9| + |11 - 11| = 9
And for the second sample:
1 2 3 4 5
|5 - 1| + |4 - 2| + |3 - 3| + |2 - 4| + |1 - 5| = 12
题解:CF上这道题要求的不是不下降序列,而是严格单调递增序列。
经过推导:$a_i < a_{i+1}$ —> $a_i <= a_{i+1} - 1$ —> $a_i - i <= a_{i+1} - (i+1)$,我们把每一项都减去i,就转换成原版了。
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 5 typedef long long LL; 6 const int N=3333; 7 LL a[N],b[N],dp[N][N]; 8 9 int main(){ 10 int n; 11 scanf("%d",&n); 12 for(int i=1;i<=n;i++){ 13 scanf("%lld",&a[i]); 14 a[i]-=i; 15 b[i]=a[i]; 16 for(int j=0;j<=n;j++) dp[i][j]=1e18; 17 } 18 sort(b+1,b+1+n); 19 for(int i=1;i<=n;i++) 20 for(int j=1;j<=n;j++) 21 dp[i][j]=min(dp[i][j-1],dp[i-1][j]+abs(a[i]-b[j])); 22 23 printf("%lld\n",dp[n][n]); 24 return 0; 25 }