POJ 3666 Making the Grade 滚动数组+状态压缩DP

来源:互联网 发布:淘宝助理外链图片 编辑:程序博客网 时间:2024/06/06 09:11

题目链接:http://poj.org/problem?id=3666

题意:输入N, 然后输入N个数,求最小的改动这些数使之成非严格递增或非严格递减

首先需要知道一个结论:一定存在一个最优解,使得整理后的数组中不存在整理前数组中不存在的数,亦即:对于每个Bi,一定存在j,使得Bi=Aj。

思路:根据数列的单调性,从小到大枚举序列的长度和最后一个数的大小

dp[i][j] 表示考虑前i个元素,最后元素为序列中 第j小元素的最优解,a[]数组存原始数组,b[]对a排序后的数组

状态转移方程:

dp[i][j] = min(dp[i-1][k]) + abs(a[i]-b[j]), (1<=k<=j)

定义m[j] = min(dp[k]) (1<=k<=j) 递归得到
             = min(m[j-1], dp[j]) (j>=2)
所以可以用状态压缩了,在枚举j的同时计算m[j]
先计算,再更新
代码如下
/*AC*/#include <cstdio>#include <iostream>#include <cmath>#include <cstring>#include <algorithm>using namespace std;int a[2005], b[2005], dp[2005], m[2005];int n;bool cmp(int i, int j){    return i > j;}int DP(){    memset(m, 0, sizeof(m));    for(int i=1; i<=n; i++)       for(int j=1; j<=n; j++){            dp[j] = m[j] + abs(a[i]-b[j]);            if(j == 1) m[j] = dp[j];            else m[j] = min(m[j-1], dp[j]);    }    int ans = dp[1];    for(int j=2; j<=n; j++)        if(dp[j] < ans) ans = dp[j];    return ans;}int main(){    while(~scanf("%d", &n)){        for(int i=1; i<=n; i++){            scanf("%d", &a[i]);            b[i] = a[i];        }        sort(b+1, b+n+1);        int ans1 = DP();        sort(b+1, b+n+1, cmp);        int ans2 = DP();        printf("%d\n", min(ans1, ans2));    }    return 0;}



 



0 0