POJ.3666 Making the Grade(DP,构造 )

Language:Making the Grade
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 14460 Accepted: 6551

Description

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

|A1 - B1| + |A2 - B2| + ... + |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

Source

USACO 2008 February Gold

本题思路首先证明一定存在最优解,使得最优解中所有数字都在原数列中出现过,我的想法是这样的,在原序列中找b序列,我们先找不下降序列,那么在arr[i]与arr[i+1]中的b序列,斜率一定是大于等于0的,即使后面arr[i+2]小于这两个数,那b[i+2]与b[i+1]构成的直线最少也是斜率为0的,斜率为零就代表一定b[i+2]=b[i+1]=某一个arr值;那要是arr[i+2]大于arr[i+1],那在当前状态最优解就是令b[i+2]=arr[i+2];如果它小于arr[i+1]不过它大于arr[i],那就令b[i+1]=b[i+2]=arr[i+2];因为这样保证了差值,又保证了b的值最小;
思路:规定状态f[i][j]是第i个序列的值是第j大序列的值;这样f[i][j]=min(f[i-1][1]...f[i-1][j])(最后一个不同点是序列i-1的取值,注意大于i-1不可能大于j);朴素做法三重循环维护最小值:


#include <algorithm>
#include <iostream>
using namespace std;
const int N = 2e3+10,inf = 0x3f3f3f3f;
int n,arr[N],brr[N],f[N][N];
int dp(){
    for(int i=1;i<=n;i++){
        //int p = minv;//f[i][j] = min(f[i-1][1-j-1])
        for(int j=1;j<=n;++j){
            int minv = inf;
            for(int k=1;k<=j;++k){
                minv = min(f[i-1][k],minv);
            }
            f[i][j] = minv + abs(arr[i]-brr[j]);
        }
    }
    int ans = inf;
    for(int i=1;i<=n;++i){
        ans = min(ans,f[n][i]);
    }
    return ans;
}
void solve(){
    cin>>n;
    for(int i=1;i<=n;++i)cin>>arr[i],brr[i] = arr[i];
    sort(brr+1,brr+1+n);
    int ans = dp();
    reverse(arr+1,arr+1+n);
    ans = min(ans,dp());
    cout<<ans<<endl;
}
int main(){
    solve();
    return 0;
}

然后我们发现,第三重循环中不就是寻找min(f[i-1,1....j]),第二重循环刚好是从1....j,恰好可以O(1)维护这样一个最小值:


#include <algorithm>
#include <iostream>
using namespace std;
const int N = 2e3+10,inf = 0x3f3f3f3f;
int n,arr[N],brr[N],f[N][N];
int dp(){
    for(int i=1;i<=n;i++){
        int p = inf;
        for(int j=1;j<=n;++j){
            p = min(f[i-1][j],p);
            f[i][j] = p + abs(arr[i]-brr[j]);
        }
    }
    int ans = inf;
    for(int i=1;i<=n;++i){
        ans = min(ans,f[n][i]);
    }
    return ans;
}
void solve(){
    cin>>n;
    for(int i=1;i<=n;++i)cin>>arr[i],brr[i] = arr[i];
    sort(brr+1,brr+1+n);
    int ans = dp();
    reverse(arr+1,arr+1+n);
    ans = min(ans,dp());
    cout<<ans<<endl;
}
int main(){
    solve();
    return 0;
}

上一篇:BlockingQueue 实现定时推送数据


下一篇:POJ3259 Wormholes(链式前向星+SPFA判断负环)