【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; }