POJ_3666
由于递增和递减是类似的,下面不妨只讨论变成递增序列的情况。
我们可以用f[i][j]表示递推到第i个数时,将第i个数变成<=j且满足序列是非减的所需要的最小的代价。由于j的范围较大,可以先离散化,不妨设最后一共有S个不同的数,那么我们要计算的就是f[N][S]。
可以得到f[i][j]=std::min(f[i-1][j]+abs(...),f[i][j-1]),式子中的...表示省略了一部分内容。如果还想优化空间的话,用滚动数组实现即可。
#include<stdio.h> #include<string.h> #include<algorithm> #define MAXD 5010 typedef long long LL; int N, S, a[MAXD], rank[MAXD]; LL f[MAXD]; struct Di { int id, v; bool operator < (const Di &t) const { return v < t.v; } }di[MAXD]; LL deal() { int i, j; for(i = 1; i <= N; i ++) di[i].id = i, di[i].v = a[i]; std::sort(di + 1, di + 1 + N); rank[di[1].id] = S = 1; for(i = 2; i <= N; i ++) { if(di[i].v != di[i - 1].v) di[++ S].v = di[i].v; rank[di[i].id] = S; } memset(f, 0, sizeof(f)); for(i = 1; i <= N; i ++) { f[0] = 0x3f3f3f3f3f3f3f3fll; for(j = 1; j <= S; j ++) f[j] = std::min(f[j - 1], f[j] + std::abs(a[i] - di[j].v)); } return f[S]; } void solve() { int i; LL ans = deal(); for(i = 1; i <= N / 2; i ++) std::swap(a[i], a[N - i + 1]); ans = std::min(ans, deal()); printf("%lld\n", ans); } int main() { while(scanf("%d", &N) == 1) { for(int i = 1; i <= N; i ++) scanf("%d", &a[i]); solve(); } return 0; }