题意:把序列A变成序列B使得B非单调递增或者非单调递减,花费为|A[i]-B[i]|,求最小花费。
(这道题数据很水,只需求非递减)
定义:dp[i][j]:前i个数不下降,第i个数凑成v[j]时的最小花费
dp[i][j]=min(dp[i−1][k]+abs(a[i]−v[j]))(1<=i<=n,0<=k<=j<m)
但我们发现这样写过后是O(n3)的时间复杂度对于数据是过不了的,那我们要优化!
用一个minn来记录1到当前j的dp[i−1][k]的最小值.我们就优化成:
dp[i][j]=minn+abs(a[i]−v[j])(1<=i<=n,0<=j<m)
j 最大可达到 1,000,000,000,枚举的话一定会 TLE,而 n 的大小最大只有 2000,那么使用离散化的思想,先对序列 a[i] 进行处理.
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#define inf 0x3f3f3f3f
using namespace std;
int a[2100];
int dp[2100][2100];
vector<int> v;
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
v.push_back(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());//离散化
int m=v.size();
for(int i=1;i<=n;i++)
{
int minn=inf;//注意初始化的位置
for(int j=0;j<m;j++)
{
minn=min(minn,dp[i-1][j]);// 记录dp[i-1][j]的最小值
dp[i][j]=minn+abs(a[i]-v[j]);
}
}
int ans=inf;
for(int i=0;i<m;i++)
ans=min(ans,dp[n][i]);//必须遍历,最后一个数不一定是最大的
printf("%d\n",ans);
return 0;
}
还可以将二维数组化为一维数组(这道题没必要)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#define inf 0x3f3f3f3f
using namespace std;
int a[2100];int ma[2100];
int dp[2100];
vector<int> v;
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
v.push_back(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());//离散化
int m=v.size();
for(int i=1;i<=n;i++)
{
int minn=inf;//注意初始化的位置
for(int j=0;j<m;j++)
{
dp[j]=ma[j]+abs(a[i]-v[j]);
minn=min(dp[j],minn);//与二维数组不同,必须要重新开一个一维数组存最小值,初始化为0
ma[j]=minn;
}
}
int ans=inf;
for(int i=0;i<m;i++)
ans=min(ans,dp[i]);//必须遍历,最后一个数不一定是最大的
printf("%d\n",ans);
return 0;
}