poj3666 线性dp

要把一个序列变成一个不严格的单调序列,求最小费用

/*
首先可以证明最优解序列中的所有值都能在原序列中找到 
以不严格单增序列为例, 
a序列为原序列,b序列为升序排序后的序列 
dp[i][j]表示处理到a中第i个数,这些数中最大值为b[j]的费用,由单调性可知第i个数肯定变为b[j]
那么dp[i][j]等价于第i个数变成b[j]的费用 
那么有 dp[i][j]=abs(b[j]-a[i])+min(dp[i-1][k]),k<=j 
*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm> 
using namespace std;
#define maxn 2005
#define ll long long 
ll n,a[maxn],b[maxn],dp[maxn][maxn]; 

ll solveup(){
    sort(b,b+n);
    memset(dp,0,sizeof dp);
    for(int i=0;i<n;i++)
        dp[0][i]=abs(a[0]-b[i]);//初始条件 
        
    for(int i=1;i<n;i++){
        ll Min=dp[i-1][0];
        for(int j=0;j<n;j++){
            Min=min(Min,dp[i-1][j]);//单调增加的集合可以直接用Min来维护 
            dp[i][j]=abs(a[i]-b[j])+Min; 
        }
    }    
    
    ll ans=dp[n-1][0];//求结果 
    for(int i=1;i<n;i++)
        ans=min(ans,dp[n-1][i]);
    return ans;     
}

int main(){
    while(scanf("%lld",&n)==1){
        for(int i=0;i<n;i++)scanf("%lld",&a[i]),b[i]=a[i];
        printf("%lld\n",solveup());
    }
}

可以用滚动数组实现,空间省了许多

/*
滚动数组解法,第一维可以省去,dp[j]表示已经完成的序列用的最大值是b[j],状态i会覆盖状态i-1并且没有影响 
dp[j]=abs(a[i]-b[j])+min(dp[k]),k<=j 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 2005
#define ll long long 

ll n,a[maxn],b[maxn],dp[maxn];
ll solve(){
    sort(b,b+n);
    for(int j=0;j<n;j++)
        dp[j]=abs(a[0]-b[j]);
    
    for(int i=1;i<n;i++){
        ll Min=dp[0];
        for(int j=0;j<n;j++){
            Min=min(Min,dp[j]);
            dp[j]=abs(a[i]-b[j])+Min;
        }
    }
    
    ll ans=dp[0];
    for(int j=1;j<n;j++) ans=min(ans,dp[j]);    
    return ans;
} 

int main(){
    while(scanf("%lld",&n)==1){
        for(int i=0;i<n;i++)scanf("%lld",&a[i]),b[i]=a[i];
        printf("%lld\n",solve());
    }
} 

猜你喜欢

转载自www.cnblogs.com/zsben991126/p/10213890.html
今日推荐