要把一个序列变成一个不严格的单调序列,求最小费用
/* 首先可以证明最优解序列中的所有值都能在原序列中找到 以不严格单增序列为例, 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()); } }