题目描述
给定长度为 N N N 的序列 A A A,构造一个长度为 N N N 的序列 B B B,满足:
- B B B 非严格单调,即 B 1 ≤ B 2 ≤ … ≤ B N B_1≤B_2≤…≤B_N B1≤B2≤…≤BN 或 B 1 ≥ B 2 ≥ … ≥ B N B_1≥B_2≥…≥B_N B1≥B2≥…≥BN;
- 最小化 S = ∑ i = 1 N ∣ A i − B i ∣ S=∑^N_{i=1}|A_i−B_i| S=∑i=1N∣Ai−Bi∣。
只需要求出这个最小值 S S S。
输入格式
第一行包含一个整数 N N N。
接下来 N N N 行,每行包含一个整数 A i A_i Ai。
输出格式
输出一个整数,表示最小 S S S 值。
数据范围
1 ≤ N ≤ 2000 1≤N≤2000 1≤N≤2000,
0 ≤ A i ≤ 1 0 6 0≤A_i≤10^6 0≤Ai≤106
输入样例
7
1
3
2
4
5
3
9
输出样例
3
题目分析
引理:在最优情况下,一定存在一种构造序列 B B B 的方式,使得 B B B 中每个元素都在 A A A 中出现过。
用数学归纳法证明:
当 N = 1 N=1 N=1 时显然成立;
假设当 N = k N=k N=k 时上述命题成立,那么当 N = k + 1 N=k+1 N=k+1时:
- 若 B k ≤ A k + 1 B_k\le A_{k+1} Bk≤Ak+1,那么令 B k + 1 = A k + 1 B_{k+1}=A_{k+1} Bk+1=Ak+1;
- 否则要么令 B k + 1 = B k B_{k+1}=B_k Bk+1=Bk;
- 要么 ∃ j ≤ k , B j = B j + 1 = … = B k = B k + 1 = v \exist j\le k,B_j=B_{j+1}=…=B_{k}=B_{k+1}=v ∃j≤k,Bj=Bj+1=…=Bk=Bk+1=v。此时设 A j , A j + 1 , … , A k + 1 A_j,A_{j+1},…,A_{k+1} Aj,Aj+1,…,Ak+1 的中位数为 m i d mid mid,若 m i d ≥ B j − 1 mid\ge B_{j-1} mid≥Bj−1,那么 v = m i d v=mid v=mid,否则 v = B j − 1 v=B_{j-1} v=Bj−1。
无论哪种情况, B B B 中所有数都在 A A A 中出现过。
状态表示:令 A A A 经过排序后组成的序列为 A ′ A' A′。设 f [ i , j ] f[i,j] f[i,j] 表示已经完成 B B B 中前 i i i 个数的构造,且 B [ i ] = A ′ [ j ] B[i]=A'[j] B[i]=A′[j]时 S S S 的最小值。
状态计算:根据倒数第二个数分类。 f [ i , j ] = m i n { f [ i − 1 , k ] + ∣ A [ i ] − A ′ [ j ] ∣ ∣ 0 ≤ k ≤ j } f[i,j]=min\{f[i-1,k]+|A[i]-A'[j]||0\le k\le j\} f[i,j]=min{ f[i−1,k]+∣A[i]−A′[j]∣∣0≤k≤j}。
类似 最长公共上升子序列,我们用一个数记录 f [ i − 1 , k ] f[i-1,k] f[i−1,k] 的最小值,因此只需两重循环。
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 1e9;
int n, res = INF;
int a[2010], b[2010], f[2010][2010];
int dp(){
for (int i = 1; i <= n; i ++) b[i] = a[i];
sort(b + 1, b + n + 1);
for (int i = 1; i <= n; i ++){
int minv = INF;
for (int j = 1; j <= n; j ++){
minv = min(minv, f[i - 1][j]);
f[i][j] = minv + abs(a[i] - b[j]);
}
}
int res = INF;
for (int i = 1; i <= n; i ++)
res = min(res, f[n][i]);
return res;
}
int main(){
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
res = dp();
reverse(a + 1, a + n + 1);
res = min(res, dp());
cout << res;
return 0;
}