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。
重點來了!!!敲黑板!!!
首先,我們需要一個性質。這個性質是最終的所有數都源自於原來的數列。
爲什麼?首先,我們證明一下。
我們從 個數開始想,假設這 個數是 。
- 這種情況,比方說
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
是最優方案之一。
之後,我們考慮給數列加一個數 。我們剛纔已經證明了, 3個數,任意排列都是最優的。現在,我們來分類討論。
爲了方便,假設都是第 種情況,,最優排列是
這種情況,顯然,對不對。比如 的最優排列是 ,。我覺得不用解釋了。
這種情況,顯然,對不對。比如 的最優排列是 ,。我覺得不用解釋了。
這種情況,顯然,對不對。比如 的最優排列是 ,。我覺得不用解釋了。
這種情況,顯然,對不對。比如 的最優排列是 ,。我覺得不用解釋了。
竟然打了 個顯然,只不過確實顯然。
然後,如果您就可以感性理解一下,我們繼續加數,肯定依然是最優。
這樣代碼就很好寫了。
大概這樣:
#include <bits/stdc++.h>
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;
}
您會發現這個算法複雜度是 的,所以,還需要優化。我們發現找最小值,會有很多的重複計算
所以,我們可以把這個算法,優化成
#include <bits/stdc++.h>
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;
}