题目链接:点击打开链接
题意:给了一个序列A【1,2,3,....n],通过改变数字使之变成有序序列C[1,2,3,...n],求最小改变量sum|ai-ci| 。
思路:不降序和不升序类似,可以先考虑不降序。可以容易证明最优的方案一定都是有序序列里的数全是原序列中的数。
如: A : 1 5 3 8 C: 1 3 3 8 或1 5 5 8 ,其他并不优
A: 4 5 9 1 2 4 3 C:1 1 1 1 2 4 4
因此枚举枚举每个位置的元素。那么第i个位置为第j的元素 dp[i][j] = min( dp[i-1][k] ) + abs( Ai - Ci) ,其中dp[i-1][k]表示前i-1个且第i-1个位置为第k(A[k]<=A[j])个元素时前i-1个位置最小的sum|ai-ci| 。
但是这样前i个的复杂度是O(n^3) 。复杂度有点大,可以优化一下,可以先把A序列排序便成有序B序列,在求dp[i-1][j]时顺便把min(dp[i-1][k])求出来,这样复杂度就降到O(n^2)的了。不懂的可以看代码,代码相对好理解点。
具体见代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
const int maxn =2* 1e3+100;
int A[maxn];
int B[maxn];
LL dp[maxn][maxn];
int n;
LL DP()
{
int i,j,k;
memset(dp,-1,sizeof(dp));
LL ans=-1;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
if(i==1)
{
dp[i][j]=abs(A[i]-B[j]);
}
else
{
dp[i][j]=dp[i-1][j]+abs(A[i]-B[j]);
}
if(j!=1) dp[i][j]=min(dp[i][j],dp[i][j-1]);//优化
if(i==n)
{
if(ans==-1) ans=dp[i][j];
else ans=min(ans,dp[i][j]);
}
}
}
return ans;
}
int main()
{
int i;
cin>> n;
for(i=1;i<=n;i++)
{
scanf("%d",&A[i]);
B[i]=A[i];
}
sort(B+1,B+n+1);
LL ans=DP(); //求不降序的
for(i=1;i<=n/2;i++)
{
int t=A[i];
A[i]=A[n-i+1];
A[n-i+1]=t;
}
ans=min(ans,DP()); //求不升序的
cout<<ans<<endl;
return 0;
}