题意:给定一个N((N<=2000))个数的序列(a_i),构建一个非严格单调的序列(b_i),使得(|a_1-b_1|+|a_2-b_2|+...+| a_N-b_N|)的值最小.
分析:首先一看到这题,应该就会有一种直觉----序列b中的数全都来自于序列a.
证明:数学归纳法.(N=1)时显然满足条件.
假设N=i-1满足条件,考虑现在填第i个数,如果(a_i>=b_{i-1}),我们可以令(b_i=a_i),满足单调性,且命题成立.而如果(a_i<b_{i-1}),要么是(b_i=b_{i-1})更优,要么得把(b_i)下调到(x),同理前面的数也要下调,而此时必然有一段(b_i)都是等于(x),而这一段达到最优一定是这一段对应的(a_i)的中位数,所以无论如何,都满足题意.证毕.
以下仅讨论构造非严格单调递增序列(b_i)的情况,非严格单调递减同理可得.
预处理(c_i)为(a_i)从小到大的数组,于是设(f[i][j])表示处理到(b_i)时,其中(b_i=c_j)时有最小值,于是我们有
(f[i][j]=min_{k=1}^j{f[i-1][k]+|a_i-c_j|})
我们可以开一个(minn)数组来维护最小值,每次更新完(f)数组后,更新(minn)数组,则(f[i][j]=minn[j]+|a_i-c_j|),(minn[j]=min(minn[j-1],f[i][j]))
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
const int N=2005;
int a[N],c[N],minn[N],f[N][N];
inline bool cmp(int &a,int &b){return a>b;}
int main(){
int n=read();
for(int i=1;i<=n;i++)c[i]=a[i]=read();
//构建单调递减队列,把c数组从小到大排序.
sort(c+1,c+n+1);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
f[i][j]=abs(a[i]-c[j])+minn[j];
minn[1]=f[i][1];
for(int j=2;j<=n;j++)
minn[j]=min(minn[j-1],f[i][j]);
}
int ans=1e9;
for(int i=1;i<=n;i++)ans=min(ans,f[n][i]);
//构建单调递减队列,把c数组从大到小排序,再求一遍
sort(c+1,c+n+1,cmp);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
f[i][j]=abs(a[i]-c[j])+minn[j];
minn[1]=f[i][1];
for(int j=2;j<=n;j++)
minn[j]=min(minn[j-1],f[i][j]);
}
for(int i=1;i<=n;i++)ans=min(ans,f[n][i]);
printf("%d
",ans);
return 0;
}