挑战程序设计竞赛2.3习题:Making the Grade POJ - 3666
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
这道题据说是有一点问题的,因为它的测试数据就是求变成最小不上升的最小代价没有从大到小。
我们假设从第1个到最后一个的土地高度构成一个数组A,那么,我们可以知道,在修改后的数组B中,所有的数都属于A,因为如果过高要挖低最少须和前面一样,如果过低要拔高则最少须和前面一样,由于前面的值为A中的值,且其他情况不需要更改土地的高度,所以B中所有的值均在A中出现过。
然后我们假设dp[i][j]表示前i个土地中最大(其实也就是第i块土地的高度)是A数组从小到大排好序后第j大的数。
为什么要这样假设?本题易得 (0 ≤ Ai ≤ 1,000,000,000),如果采用传统的方法dp[i][j],i表示前i种,j表示最大的高度是j,那么算法复杂度就是O(AN)由于A最大1e9,定会超时,所以这道题解题的关键就是离散化解本题,所谓的离散化(百度百科):
#include <stdio.h> #include <algorithm> using namespace std; const int INF = 0x3fffffff; int a[2005];//原数组 int b[2005];//原数组排完序的数组 long long dp[2005][2005];//dp[i][j]前i个中第i块土地为j高度的最小花费 long long min_old = INF;//前 j - 1项的最小值 long long ans = INF;//答案 int main(void) { int n; scanf("%d", &n); for(int i = 0; i < n; i++) { scanf("%d", &a[i]); b[i] = a[i]; } sort(b, b + n); for(int i = 0; i < n; i++) dp[0][i] = abs(b[i] - a[0]);//一开始让第一块地值为i,所以dp[0][i]均为abs(b[i] - a[0]),b[i]:排完序第i大高度,a[0]:第一块地的初始高度 for(int i = 1; i < n; i++)//从2块地开始(因为i从0开始)到n块地 { min_old = INF;//初始化前i - 1块地的最大高度为a[0]到a[j - 1]时那一块最小 for(int j = 0; j < n; j++) { min_old = min(min_old, dp[i - 1][j]);//前i - 1块地的最大高度为a[0]到a[j - 1]时最小的花费与前i - 1块地最大高度为a[j]时哪个花费小 dp[i][j] = min_old + abs(b[j] - a[i]); } } for(int i = 0; i < n; i++) ans = min(ans, dp[n - 1][i]);//答案就在前n块中第n块的高度为a[i]时(因为最后一块什么高度都有可能,都满足从小到大的条件,答案只是取其中最小值) printf("%lld\n", ans); return 0; }