Making the Grade

题目链接:https://vjudge.net/problem/POJ-3666#author=0258

题意:给你一个数组,你每进行一次操作,可以使a[i]+1或a[i]-1。现在要求你进行尽可能少的操作,使得a[i]>=a[i+1]  (0<i<n)或者a[i]<=a[i+1] (0<i<n)。

思路:线性dp,设dp[i][j]表示前i个数满足第一种或者第二种情况最小的操作数。那转移方程为dp[i][j]=min(dp[i][j],dp[i-1][j]+abs(a[i]-j));

由于a[i]较大,所以先要进行离散。

#include<bits/stdc++.h>
/*#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<queue>*/
using namespace std;
typedef long long ll;
#define INF 0x7ffffff
ll dp[2005][2005];
int a[2005],b[2005];
map<int,int> mp;
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        b[i]=a[i];
    }
    sort(b+1,b+n+1);
    int m=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=m;i++)
        mp[b[i]]=i;
    for(int i=1;i<=n;i++)
        a[i]=mp[a[i]];
    for(int i=0;i<=n+1;i++)
        for(int j=0;j<=n+1;j++)
        dp[i][j]=5000000000000;
    for(int i=1;i<=m;i++)
    {
        dp[1][i]=abs(b[a[1]]-b[i]);
        dp[1][i]=min(dp[1][i],dp[1][i-1]);
    }
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            dp[i][j]=min(dp[i][j],dp[i-1][j]+abs(b[a[i]]-b[j]));
            dp[i][j]=min(dp[i][j],dp[i][j-1]);
        }
    }
    ll ans=dp[n][m];
    for(int i=0;i<=n+1;i++)
        for(int j=0;j<=n+1;j++)
        dp[i][j]=5000000000000;
    for(int i=m;i>=1;i--)
    {
        dp[1][i]=abs(b[a[1]]-b[i]);
        dp[1][i]=min(dp[1][i],dp[1][i+1]);
    }
    for(int i=2;i<=n;i++)
    {
        for(int j=m;j>=1;j--)
        {
            dp[i][j]=min(dp[i][j],dp[i-1][j]+abs(b[a[i]]-b[j]));
            dp[i][j]=min(dp[i][j],dp[i][j+1]);
        }
    }
    cout<<min(ans,dp[n][1])<<endl;
}
原文地址:https://www.cnblogs.com/zcb123456789/p/13691863.html