滑雪場設計
滑雪場設計
農夫約翰的農場上有 $N$ 個山峰,每座山的高度都是整數。
在冬天,約翰經常在這些山上舉辦滑雪訓練營。
不幸的是,從明年開始,國家將實行一個關於滑雪場的新稅法。
如果滑雪場的最高峰與最低峰的高度差大於 $17$,國家就要收稅。
為了避免納稅,約翰決定對這些山峰的高度進行修整。
已知,增加或減少一座山峰 $x$ 單位的高度,需要花費 $x^{2}$ 的金錢。
約翰只願意改變整數單位的高度,且每座山峰只能修改一次。
請問,約翰最少需要花費多少錢,才能夠使得最高峰與最低峰的高度差不大於 $17$。
輸入格式
第一行包含整數 $N$。
接下來 $N$ 行,每行包含一個整數,表示一座山的高度。
輸出格式
輸出一個整數,表示最少花費的金錢。
資料範圍
$1 \leq N \leq 1000$,
資料保證,每座山的初始高度都在 $0 \sim 100$ 之間。
輸入樣例:
5 20 4 1 24 21
輸出樣例:
18
樣例解釋
最佳方案為,將高度為 $1$ 的山峰,增加 $3$ 個單位高度,將高度為 $24$ 的山峰,減少 $3$ 個單位高度。
解題思路
用一個數軸表示山峰的高度。
下面證明最終修改完成後,所有山峰的高度都會在$\left[ {0, 100} \right]$這個區間內。最優解中不會存在某個山峰的高度小於$0$或大於$100$。
假設最優解中所有山峰的高度都小於$0$,如下圖。我們把所有山峰的高度都變為$0$。
對於所有的山峰,它的一開始的高度必然在$\left[ {0, 100} \right]$內的。在最優解中,我們是把這個山峰的高度變為小於$0$的,現在我們把這個山峰的高度變為$0$,可以發現代價變小了。因此我們可以不要把山峰的高度變到最優解的位置,而變到$0$的位置,總代價會變小,並且滿足要求(高度差不超過$17$),因此可以構造出一個更好的方案,就與最優解矛盾了。
另外一種情況是最優解中有部分山峰的高度是小於$0$的,如下圖。同樣的,我們把高度小於$0$的山峰變為$0$。
對於高度小於$0$的山峰,與上面的分析一樣,變成到$0$後代價會減少,因此可以構造總代價更小的合法方案,就與最優解矛盾了。
同理可證,所有的山峰的高度的最大值不會超過$100$(把超過$100$的山峰都變成$100$,發現總代價會變小)。
所以在最優解中,所有山峰的高度必然在$\left[ {0, 100} \right]$內。
因此我們可以在$\left[ {0, 100} \right]$中,列舉所有長度為$17$的區間,一共有$84$個這樣的區間。
對於列舉的每個區間,我們會列舉所有的山峰。
- 如果某個山峰的高度在這個區間內,那麼這個山峰就不需要修改。
- 如果某個山峰的高度小於區間的左端點,那麼就需要把這個山峰的高度修改為左端點的大小,這一定會是最小的代價。
- 如果某個山峰的高度大於區間的右端點,那麼就需要把這個山峰的高度修改為右端點的大小,這一定會是最小的代價。
這是因為每個山峰是獨立的,每個山峰的修改都不會影響到其他的山峰。因此如果我們想讓總代價最小,只需要讓每個山峰的代價取到最小。
這題的本質是找到一個區間,使得所有的點到這個區間的距離最小。
AC程式碼如下:
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 5 const int N = 1010; 6 7 int a[N]; 8 9 int main() { 10 int n; 11 scanf("%d", &n); 12 for (int i = 0; i < n; i++) { 13 scanf("%d", a + i); 14 } 15 16 int ret = 2e9; 17 for (int i = 0; i + 17 <= 100; i++) { // 列舉所有長度為17的區間 18 int l = i, r = i + 17, t = 0; 19 for (int j = 0; j < n; j++) { 20 if (a[j] < l) t += (l - a[j]) * (l - a[j]); // 山峰的高度小於左端點,則變到左端點 21 else if (a[j] > r) t += (a[j] - r) * (a[j] - r); // 山峰的高度大於右端點,則變到右端點 22 } 23 ret = min(ret, t); 24 } 25 26 printf("%d", ret); 27 28 return 0; 29 }
參考資料
AcWing 1353. 滑雪場設計(寒假每日一題):https://www.acwing.com/video/2327/