給一個數列,長度不超過 5000,每次能夠將其中的一個數加 1 或者減 1,問,最少須要多少次操做,才能使得這個數列單調不降ios
數列中每一個數爲 -109~109 中的一個數數組
先這樣考慮:若是操做的次數最少,那麼最終獲得的不降的數列,必然是由原始數列中的數組成的,具體的證實能夠使用反證法ide
知道了上面講述的性質,這題就好搞了spa
先將原始數列(設爲 A,共 n 個數)中全部的數去重並從小到達排序,保存在另外一個數列中(設爲 B,共 m 個數)code
定義狀態:f[i][j] 表示將原始數列中的前 i 個數變成單調不降,第 i 個數最多爲 B[j] 的最少操做次數blog
初始化 f[0][0]=abs(A[0]-B[0]), f[0][i]=min{ f[0][i-1], abs(A[0]-B[i]) }排序
初始化 f[i][0]=f[i-1][0]+abs(A[i]-A[0]),那麼有:f[i][j]=min( f[i][j-1], f[i-1][j]+abs(A[i]-B[j]) )內存
目標狀態:f[n-1][m-1]get
因爲內存的關係,能夠使用滾動數組string
1 #include <iostream>
2 #include <cstring>
3 #include <cstdio>
4 #include <algorithm>
5 #include <vector>
6
7 using namespace std;
8
9 typedef long long LL;
10 const int N=5003;
11 const LL INF=(1LL)<<60;
12
13 LL f[2][N], A[N], B[N];
14 vector <int> tub;
15 int n, m;
16
17 int main() {
18 while(scanf("%d", &n)!=EOF) {
19 tub.clear(), m=0;
20 for(int i=0; i<n; i++) {
21 scanf("%I64d", &A[i]);
22 tub.push_back(A[i]);
23 }
24 sort(tub.begin(), tub.end());
25 for(int i=0; i<n; i++) {
26 while(i+1<n && tub[i]==tub[i+1]) i++;
27 B[m++]=tub[i];
28 }
29 memset(f, 0, sizeof f);
30 f[0][0]=abs(A[0]-B[0]);
31 for(int i=1; i<m; i++) f[0][i]=min(f[0][i-1], abs(A[0]-B[i]));
32 for(int i=1; i<n; i++) {
33 int cur=i&1, lst=cur^1;
34 f[cur][0]=f[lst][0]+abs(A[i]-B[0]);
35 for(int j=1; j<m; j++) f[cur][j]=min(f[cur][j-1], f[lst][j]+abs(A[i]-B[j]));
36 }
37 printf("%I64d\n", f[(n-1)&1][m-1]);
38 }
39 return 0;
40 }