题意:
给定长度为N的序列A,构造一个长度为N的序列B,满足:
1.B非严格单调,即B1 <= B2 <= ... <= BN 或 B1 >= B2 >= ... >= BN.
2.最小化 S = (累加)| Ai - Bi |,只需要求出这个最小值S.
1 <= N <= 2000,1 <= | Ai | <= 1e9
思路:
可以证明,我们能够只用A中的数字就可以构造出符合条件的B【虽然我不会证明,但是我觉得如果再遇到这种题,我应该也会这么猜的吧】。
既然可以用A中数字构造B,那么可以考虑先对A进行一个排序,本题单调增和单调减都需要求出,然后再比较最小值。【不过,写完之后发现只考虑单调增就可以通过本题,考虑单调减不可行】
这里只考虑单调增,因此用一个num数组将A复制下来,将num数组排升序,并且去重。由于n比较小,但是Ai很大,因此将Ai映射到数组中,进行离散化操作。
令dp[ i ][ j ]为B序列中最后一个数为num[ j ],并且前 i 个数字的cost最小,则
dp[ i ][ j ] = min(dp[ i-1 ][ x ]) + abs(a[ i ] - num[ j ])
由于更新dp[ i ][ j ]的时候,j始终不变,因此遍历过程中取一个最小值,进行依次更新,即可将On^3的复杂度降为On^2.
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define rep(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
int n;
int a[2500],num[2500];
int dp[2500][2500]; //dp[i][j]表示前i个数字花费最小,并且最后以num[j]结尾
//由于数字的范围很大,但是数字个数比较少,因此进行离散化,即把数字映射到区间上
int main()
{
while(~scanf("%d",&n))
{
rep(i,1,n){
scanf("%d",&a[i]);
num[i] = a[i];
}
sort(num+1,num+1+n);
int m = unique(num+1,num+1+n)-num-1;
memset(dp,0,sizeof dp);
rep(i,1,n)
{
//由于i不变,因此可以记录最小值
int tmp = dp[i-1][1];
rep(j,1,m)
{
tmp = min(tmp,dp[i-1][j]);
//dp[i][j] = min(dp[i-1][x])+abs(a[i]-num[j]); x <= j,保证升序
dp[i][j] = tmp+abs(a[i]-num[j]);
}
}
int ans = 0x3f3f3f3f;
rep(i,1,m)
ans = min(ans,dp[n][i]);
printf("%d\n",ans);
}
return 0;
}