A straight dirt road connects two fields on FJ's farm, but it changes elevation more than FJ would like. His cows do not mind climbing up or down a single slope, but they are not fond of an alternating succession of hills and valleys. FJ would like to add and remove dirt from the road so that it becomes one monotonic slope (either sloping up or down).
You are given N integers A1, ... , AN (1 ≤ N ≤ 2,000) describing the elevation (0 ≤ Ai ≤ 1,000,000,000) at each of N equally-spaced positions along the road, starting at the first field and ending at the other. FJ would like to adjust these elevations to a new sequence B1, . ... , BN that is either nonincreasing or nondecreasing. Since it costs the same amount of money to add or remove dirt at any position along the road, the total cost of modifying the road is
| A 1 - B 1| + | A 2 - B 2| + ... + | AN - BN |
Please compute the minimum cost of grading his road so it becomes a continuous slope. FJ happily informs you that signed 32-bit integers can certainly be used to compute the answer.
Input
* Line 1: A single integer: N
* Lines 2..N+1: Line i+1 contains a single integer elevation: Ai
Output
* Line 1: A single integer that is the minimum cost for FJ to grade his dirt road so it becomes nonincreasing or nondecreasing in elevation.
Sample Input
7 1 3 2 4 5 3 9
Sample Output
3
农夫约翰想修一条尽量平缓的路,路的每一段海拔是A_i,修理后是B_i,花费|A_i – B_i|,求最小花费。
平缓的意思是海拔单调增或单调减(非严格,下同),放在《2.3 记录结果再利用的“动态规划” 需稍加思考的题目》的主要目的大概是让我们破解这个“到底是增好还是减好”的局。
这一道题目要先知道一个地方,那就是最后的序列一定是原先序列中的数,参考大佬的讲解:
记原来的数从小到大排序后分别是a1 a2 a3⋯ana1 a2 a3⋯an 修改后从左到右分别是b1 b2 b3⋯bnb1 b2 b3⋯bn. 为了方便描述,在数轴上标出这些点,称为关键点。
假设存在as<bi<=bi+1<=⋯<=bj<as+1as<bi<=bi+1<=⋯<=bj<as+1
情况一:如果这些b都相等,那么把这些b都改成asas或者as+1as+1 肯定会有一种更优。
情况二:如果不全相等,那么肯定存在 bp bp+1 bp+2⋯bqbp bp+1 bp+2⋯bq,他们的值相等,那么把他们移到左边的关键点或者右边的关键点,肯定有一种会更加优. 不断这样移动,最后就变成情况一了。
综上至少存在一种最优方案,最终的所有数都是原来的某个数。
因此可以离散化之后做dp,dp[i][j]表示把前i个数变成单调增(不严格)且第i个数变成原来第j大的数的最小代价。
然后知道这一点之后,那就需要dp一下了,首先定义dp[i][j]为前i个数中以b[j]结尾的最小花费
dp[i][j]=min{dp[i-1][1...j]}+abs(a[i]-b[j]).
单调减(不严格)的情况也一样,更加方便的是可以把原数组倒转后做单调增的dp。
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1e5+5;
const int MOD=1e6;
const int INF= 0x3f3f3f3f;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define init(a,b) memset(a,b,sizeof a)
#define Abs(a) ((a)>0?a:-(a))
//int dp[N];
ll a[N];
ll b[N];
ll dp[3][2010];
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif // ONLINE_JUDGE
int n,m;
scanf("%d",&n);
rep(i,1,n)
scanf("%d",&a[i]),b[i]=a[i];
sort(b+1,b+1+n);
rep(i,1,n){
ll minn=dp[i-1][1];
rep(j,1,n){
minn=min(minn,dp[i-1][j]);
dp[i][j]=Abs(b[j]-a[i])+minn;
}
}
printf("%lld\n",*min_element(dp[n]+1,dp[n]+1+n));
return 0;
}
因为每一次只用到前一次的dp方程,因此可以用滚动数组来优化