离散化+dp,好神奇的做法,将一个序列a变成b,并要求每一个元素|a[i] - b[i]|的绝对值尽量小,求最小的绝对值之和。。
如果能想到离散化,这就是一道非常水的题目了。。
因为元素最大为INF,因此不能作为下标,如果将所有值离散化,就可以进行状态转移了。
首先将原来数组排序去重后存到一个t数组里,然后dp[i][j]中的第一维i代表当前到了第几个元素,j表示最后一个数的大小,因为要满足非递减关系,所以只取前一项中最后一个数小于等于当前j的最小值就行。
状态转移:dp[i][j] = abs(t[j] - a[i]) + dp[i - 1][j]。
下面附代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn = 2000 + 10;
int a[maxn];
int b[maxn];
long long dp[maxn][maxn];
int n;
int nn;
const long long INF = 0x7fffffffffffffff;
int main()
{
while(~scanf("%d", &n))
{
for(int i = 0; i < n; ++i)
{
scanf("%d", &a[i]);
b[i] = a[i];
}
sort(b, b + n);
nn = unique(b, b + n) - b;
memset(dp, 0, sizeof(dp));
for(int i = 0; i < nn; ++i)
dp[0][i] = abs(a[0] - b[i]);
for(int i = 1; i < n; ++i)
{
long long MIN = dp[i - 1][0];
dp[i][0] = abs(a[i] - b[0]) + MIN;
for(int j = 1; j < nn; ++j)
{
MIN = min(dp[i - 1][j], MIN);
dp[i][j] = abs(a[i] - b[j]) + MIN;
}
}
long long MIN = INF;
for(int i = 0; i < nn; ++i)
{
MIN = min(MIN, dp[n - 1][i]);
}
printf("%I64d\n", MIN);
}
}