题意:有若干个数,然后可以花费i的代价让某个数+i或者-i。
现在要求让你把序列排成不升或者不降,问最小代价。
题解:
首先可以证明,最优花费下最后所有的数都可以是现在的某个数:
证:如果两个数调整后在两个数中间,那么可以把两个数都变为其中一个数,而代价显然是等同的。
这个出来后就好做了。
我们可以先离散化一下,然后f[i][j]表示第i个数变为j时1~i这些数保持非严格单调的最小花费
转移时f[i][j]不必n*n枚举,可以维护一个前缀最优(非常水),然后O(1)转移。
然后做一遍非升,一遍非降出解。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 2500
#define inf 0x3f3f3f3f3f3f3f3fll
using namespace std;
struct LSH
{
int x,id;
bool operator < (const LSH &a)const{return x<a.x;}
}lsh[N];
int cnt,n,w[N];
long long f[N][N];
int main()
{
// freopen("test.in","r",stdin);
int i,j,k;
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&lsh[i].x),lsh[i].id=i;
sort(lsh+1,lsh+n+1),lsh[0].x=-1;
for(i=1;i<=n;i++)
{
if(lsh[i].x!=lsh[i-1].x)lsh[++cnt].x=lsh[i].x;
w[lsh[i].id]=cnt;
}
// 离散化结束,lsh中对应原值
//升序
for(i=1;i<=n;i++)
{
long long temp=inf;
for(j=0;j<=cnt;j++)
{
temp=min(temp,f[i-1][j]);
f[i][j]=temp+abs(lsh[w[i]].x-lsh[j].x);
}
}
long long ans1=inf;
for(i=0;i<=cnt;i++)ans1=min(ans1,f[n][i]);
//降序
for(i=1;i<=n;i++)
{
long long temp=inf;
for(j=cnt;j>=0;j--)
{
temp=min(temp,f[i-1][j]);
f[i][j]=temp+abs(lsh[w[i]].x-lsh[j].x);
}
}
long long ans2=inf;
for(i=0;i<=cnt;i++)ans2=min(ans2,f[n][i]);
cout<<min(ans1,ans2)<<endl;
return 0;
}