【luogu P2893】 [USACO08FEB]修路Making the Grade

传送门:https://www.luogu.org/problemnew/show/P2893

题目中的“原来的路的每一段海拔是A_i,修理后是B_i,花费|A_i – B_i|。我们要求修好的路是单调不升或者单调不降的。求最小花费。”第一眼看起来像上升子序列(显然不是),这是道线性dp的比较简单的题。首先一个合法的方案一定可以满足构造出的序列B使得∀x∈B 有x∈A,

所以B序列一定是由多个x(x∈A)的连续段构成的,那么先将A离散化降低消耗,设f[i][j]为做完前i个数,最后一个数为j的最小花费,则有:

f[i][j]=min(f[i−1][k]+∣A[i]−j∣)。

时间复杂度为O(n^2),不会超时。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstring>
#include<vector>
using namespace std;
const int maxn = 2005;
int a[maxn], num[maxn], c[maxn];
int f[maxn][maxn];
int n;
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        num[i] = a[i];
    }
    sort(num + 1,num + n + 1);
    int m = unique(num + 1,num + n + 1) - num - 1;
    for(int i = 1; i <= n; i++)
        c[i] = lower_bound
               (num + 1,num + n + 1,a[i]) - num;
    memset(f,0x3f,sizeof(f));
    f[0][0] = 0;
    for(int i = 1; i <= n; i++) {
        int temp = f[i - 1][0];
        for(int j = 1; j <= m; j++) {
            temp = min(temp,f[i - 1][j]);
            f[i][j] = temp + abs(a[i] - num[j]);
        }
    }
    int ans = 1 << 30;
    for(int i = 1; i <= m; i++)
        ans = min(ans,f[n][i]);
    printf("%d\n", ans);
    return 0;
}
View Code

 

posted @ 2019-06-09 11:00  机器闵  阅读(135)  评论(0编辑  收藏  举报