FJ 打算好好修一下农场中某条凹凸不平的土路。按奶牛们的要求,修好后的
路面高度应当单调上升或单调下降,也就是说,高度上升与高度下降的路段不能
同时出现在修好的路中。
整条路被分成了 N 段,N 个整数 A_1, … , A_N (1 <= N <= 2,000)依次描述
了每一段路的高度(0 <= A_i <= 1,000,000,000)。FJ 希望找到一个恰好含 N 个
元素的不上升或不下降序列 B_1, … , B_N,作为修过的路中每个路段的高度。
由于将每一段路垫高或挖低一个单位的花费相同,修路的总支出可以表示为:
|A_1 - B_1| + |A_2 - B_2| + … + |A_N - B_N|
请你计算一下,FJ 在这项工程上的最小支出是多少。FJ 向你保证,这个支出
不会超过 2^31-1。
重点来了!!!敲黑板!!!
首先,我们需要一个性质。这个性质是最终的所有数都源自于原来的数列。
为什么?首先,我们证明一下。
我们从 3 3 3 个数开始想,假设这 3 3 3 个数是 a , b , c a,b,c a,b,c。
7 8 9
,之后,我们的方案是 7 8 9
是最优方案之一。7 6 5
,之后,我们的方案是 7 6 5
是最优方案之一。1 9 7
,之后,我们的方案是 1 7 9
是最优方案之一。8 9 7
,之后,我们的方案是 9 9 7
是最优方案之一。7 5 9
,之后,我们的方案是 5 5 9
是最优方案之一。7 5 6
,之后,我们的方案是 7 5 5
是最优方案之一。之后,我们考虑给数列加一个数 d d d。我们刚才已经证明了, a , b , c a,b,c a,b,c 3个数,任意排列都是最优的。现在,我们来分类讨论。
为了方便,假设都是第 1 1 1 种情况, a < b < c a < b < c a<b<c,最优排列是 a , b , c a,b,c a,b,c
这种情况,显然,对不对。比如 a , b , c a,b,c a,b,c 的最优排列是 3 , 5 , 7 , 9 3,5,7,9 3,5,7,9, d = 2 d=2 d=2。我觉得不用解释了。
这种情况,显然,对不对。比如 a , b , c a,b,c a,b,c 的最优排列是 3 , 5 , 7 , 9 3,5,7,9 3,5,7,9, d = 4 d=4 d=4。我觉得不用解释了。
这种情况,显然,对不对。比如 a , b , c a,b,c a,b,c 的最优排列是 3 , 5 , 7 , 9 3,5,7,9 3,5,7,9, d = 6 d=6 d=6。我觉得不用解释了。
这种情况,显然,对不对。比如 a , b , c a,b,c a,b,c 的最优排列是 3 , 5 , 7 , 9 3,5,7,9 3,5,7,9, d = 10 d=10 d=10。我觉得不用解释了。
竟然打了 4 4 4 个显然,只不过确实显然。
然后,如果您就可以感性理解一下,我们继续加数,肯定依然是最优。
这样代码就很好写了。
大概这样:
#include
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
template<typename T>inline void write(T x){
if(x<0)putchar('-'),x*=-1;
if(x>9)write(x/10);
putchar(x%10+48);
}
template<typename T>inline void writen(T x){
write(x);
puts("");
}
const int MAXN=2e3+10;
int n,a[MAXN],b[MAXN],ans,f[MAXN][MAXN];
bool cmp1(int x,int y){
return x>y;
}
bool cmp2(int x,int y){
return x<y;
}
int getans(){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
f[i][j]=INT_MAX;
for(int k=1;k<=j;k++)
f[i][j]=min(f[i][j],f[i-1][k]+abs(b[i]-a[j]));
}
}
int ans=INT_MAX;
for(int i=1;i<=n;i++)ans=min(ans,f[n][i]);
return ans;
}
int main(){
read(n);
for(int i=1;i<=n;i++)read(a[i]),b[i]=a[i];
sort(a+1,a+n+1,cmp1);
ans=getans();
sort(a+1,a+n+1,cmp2);
ans=min(ans,getans());
cout<<ans;
return 0;
}
您会发现这个算法复杂度是 O ( n 3 ) O(n^3) O(n3) 的,所以,还需要优化。我们发现找最小值,会有很多的重复计算
所以,我们可以把这个算法,优化成 O ( n 2 ) O(n^2) O(n2)
#include
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
template<typename T>inline void write(T x){
if(x<0)putchar('-'),x*=-1;
if(x>9)write(x/10);
putchar(x%10+48);
}
template<typename T>inline void writen(T x){
write(x);
puts("");
}
const int MAXN=2e3+10;
int n,a[MAXN],b[MAXN],ans,f[MAXN][MAXN];
bool cmp1(int x,int y){
return x>y;
}
bool cmp2(int x,int y){
return x<y;
}
int getans(){
for(int i=1;i<=n;i++){
int ans=INT_MAX;
for(int j=1;j<=n;j++){
ans=min(ans,f[i-1][j]);
f[i][j]=ans+abs(b[i]-a[j]);
}
}
int ans=INT_MAX;
for(int i=1;i<=n;i++)ans=min(ans,f[n][i]);
return ans;
}
int main(){
read(n);
for(int i=1;i<=n;i++)read(a[i]),b[i]=a[i];
sort(a+1,a+n+1,cmp1);
ans=getans();
sort(a+1,a+n+1,cmp2);
ans=min(ans,getans());
cout<<ans;
return 0;
}