[POJ 3666]Making the Grade[DP]

题目链接:[POJ 3666]Making the Grade[DP]
题意分析:
含有n个数的序列,允许对序列中的任意一个数加或者减,问:最少更改多少值,使得原序列变成单调递增或者单调递减?
解题思路:
对于一个数字的修改,我们将它修改成序列中有的数是最好的选择(我是靠找不到反例来理解的= =)。那么我们就可以先把这些数字拷贝出来排个序,就有接下来:
设状态dp[i][j]为到第i个数,以b[j]为结尾更改的最少价值。那么有转移方程

dp[i][j]=min(dp[i1][k])+|a[i]b[j]|(k=0j)
这个方程是三方的,对于此题显然会T的。注意到min(dp[i-1][k])是前j项的最小值,那么在转移的时候维护这个最小值即可。这样可以降到平方的复杂度。
个人感受:
前n项求和,求最小值等等等,都能这么优化,2333
具体代码如下:

#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<string>
#define ll long long
using namespace std;

const int INF = 0x7f7f7f7f;
const int MAXN = 2e3 + 111;

int a[MAXN], b[MAXN];
int dp[MAXN][MAXN];

bool cmp(int a, int b) {
    return a > b;
}

int main()
{
    int n; cin >> n;
    for (int i = 0; i < n; ++i) cin >> a[i], b[i] = a[i];

    sort(b, b + n);
    for (int i = 0; i < n; ++i) dp[0][i] = abs(a[0] - b[i]);
    int ans1 = INF;
    for (int i = 1; i < n; ++i) {
        int pre = dp[i - 1][0];
        for (int j = 0; j < n; ++j) {
            pre = min(pre, dp[i - 1][j]);
            dp[i][j] = pre + abs(a[i] - b[j]);
        }
    }
    for (int i = 0; i < n; ++i) {
        ans1 = min(ans1, dp[n - 1][i]);
    }

    sort(b, b + n, cmp);
    for (int i = 0; i < n; ++i) dp[n - 1][i] = abs(a[n - 1] - b[i]);
    int ans2 = INF;
    for (int i = n - 2; i >= 0; --i) {
        int pre = dp[i + 1][n - 1];
        for (int j = n - 1; j >= 0; --j) {
            pre = min(pre, dp[i + 1][j]);
            dp[i][j] = pre + abs(a[i] - b[j]);
        }
    }
    for (int i = 0; i < n; ++i) {
        ans2 = min(ans2, dp[0][i]);
    }

    cout << min(ans1, ans2) << '\n';
    return 0;
}

你可能感兴趣的:(dp)