1. 程式人生 > >POJ-3666 Making the Grade 【動態規劃DP+滾動陣列】

POJ-3666 Making the Grade 【動態規劃DP+滾動陣列】

題目傳送門

題目:輸入n個數,第i個數字的值為a[i],把第i個數變為j的代價為a[i]-j的絕對值,求把這n個數組成的數列變成單調數列的最小代價。

題解:dp[i][j]表示前i個數最大值為b[j]時的最小代價,即第i個數在總數列中的值為第j小的時候的最小代價。

動態轉移方程:dp[i][j]=minn+Abs(a[i]-b[j])。

其中minn=min(dp[i-1][1],dp[i-1][2]......dp[i-1][j]),第i個數在總序列中的值為第j小的時候的最小代價=第i-1個數在總序列中的值小於等於第j小的時候的最小代價 + 把第i個數變為在總數列中的值為第j小的時候的代價

用滾動陣列minn儲存第i-1個數在總序列中的值小於等於第j小的時候的最小代價。

結果序列可以是遞增序列也可以是遞減序列,可能是題目測試資料的問題,只考慮遞增序列就過了。

AC程式碼:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define inf 0x7fffffff
#define Abs(a) ((a)>0?(a):-(a))
typedef long long ll;
const int maxn=2200;

int n;
ll a[maxn],b[maxn];
ll dp[maxn][maxn];//d[i][j] 代表前i個數最大為b[j]時的最小代價
int main()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&a[i]);
        b[i]=a[i];
    }
    sort(b+1,b+1+n);
    memset(dp,0,sizeof(dp));
    for(int i=1; i<=n; i++)
    {
        ll minn=dp[i-1][1];
        for(int j=1; j<=n; j++)
        {
            minn=min(minn,dp[i-1][j]);
            dp[i][j]=minn+Abs(a[i]-b[j]);//d[i][j]=min(d[i-1][k])+abs(a[i]-b[j]),(0<k<=j)
        }
    }
    ll ans=inf;
    for(int i=1; i<=n; i++)
    {
        ans=min(ans,dp[n][i]);
    }
    cout<<ans<<endl;
    return 0;
}