题意:给定一个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\n",ans);
return 0;
}