题目链接:http://poj.org/problem?id=3666
题意:求将一个序列调整成纯单调(可以相等)的序列所需要的最小代价
解题思路:
分两种情况讨论:
①不减序列:dp[i][j]表示前0~i个数的最大值为这i个数中第j大值需要改变的量
因为序列a[i]的最大值可能达到1e9的大小,所以需要用用到离散化,将序列a从小到大顺序放在数组b中,j表示a序列中第j大的数值
转移方程为 dp[i][j]=abs(a[i]-b[j])+min(dp[i-1][k]),1<=k<=j,
对于min(dp[i-1][k])的求法不需要遍历,只需要在j推进过程中不断更新即可
ll solveup(){ //上升序列
ll minv;
ll ans;
for(int i=1;i<=n;i++)
dp[1][i]=abs(a[1]-b[i]);
for(int i=2;i<=n;i++){
minv=dp[i-1][1];
for(int j=1;j<=n;j++){
minv=min(minv,dp[i-1][j]);
dp[i][j]=abs(a[i]-b[j])+minv;
}
}
ans=dp[n][1];
for(int i=1;i<=n;i++)
ans=min(ans,dp[n][i]);
return ans;
}
②不增序列:dp[i][j]表示前0~i个数的最大值为这i个数中第j小值需要改变的量
a序列按从大到小顺序保存在b数组中,其他同理
ll solvedown(){
ll minv;
ll ans;
for(int i=1;i<=n;i++)
dp[1][i]=abs(a[1]-b[i]);
for(int i=2;i<=n;i++){
minv=dp[i-1][1];
for(int j=1;j<=n;j++){
minv=min(minv,dp[i-1][j]);
dp[i][j]=abs(a[i]-b[j])+minv;
}
}
ans=dp[n][1];
for(int i=1;i<=n;i++)
ans=min(ans,dp[n][i]);
return ans;
}
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
#define ll long long
int n;
ll a[2200];
ll b[2200];
ll dp[2200][2200]; //前0~i个数的最大值为这i个数中第j大值需要改变的量
//前0~i个数的最大值为这i个数中第j小值需要改变的量
bool cmp(int a,int b){
return a>b;
}
ll solveup(){ //上升序列
ll minv;
ll ans;
for(int i=1;i<=n;i++)
dp[1][i]=abs(a[1]-b[i]);
for(int i=2;i<=n;i++){
minv=dp[i-1][1];
for(int j=1;j<=n;j++){
minv=min(minv,dp[i-1][j]);
dp[i][j]=abs(a[i]-b[j])+minv;
}
}
ans=dp[n][1];
for(int i=1;i<=n;i++)
ans=min(ans,dp[n][i]);
return ans;
}
ll solvedown(){
ll minv;
ll ans;
for(int i=1;i<=n;i++)
dp[1][i]=abs(a[1]-b[i]);
for(int i=2;i<=n;i++){
minv=dp[i-1][1];
for(int j=1;j<=n;j++){
minv=min(minv,dp[i-1][j]);
dp[i][j]=abs(a[i]-b[j])+minv;
}
}
ans=dp[n][1];
for(int i=1;i<=n;i++)
ans=min(ans,dp[n][i]);
return ans;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+1+n);
int ans1=solveup();
sort(b+1,b+1+n,cmp);
int ans2=solvedown();
cout<<min(ans1,ans2)<<endl;
return 0;
}