1. 程式人生 > 其它 >滑雪場設計

滑雪場設計

滑雪場設計

農夫約翰的農場上有 $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$後代價會減少,因此可以構造總代價更小的合法方案,就與最優解矛盾了。

  

綜上所述,不會有任何一個山峰的高度會小於$0$。因此在最優解裡面,所有的山峰的最小值一定大於等於$0$。

  同理可證,所有的山峰的高度的最大值不會超過$100$(把超過$100$的山峰都變成$100$,發現總代價會變小)。

  所以在最優解中,所有山峰的高度必然在$\left[ {0, 100} \right]$內。

  因此我們可以在$\left[ {0, 100} \right]$中,列舉所有長度為$17$的區間,一共有$84$個這樣的區間。

  對於列舉的每個區間,我們會列舉所有的山峰。

  1. 如果某個山峰的高度在這個區間內,那麼這個山峰就不需要修改。
  2. 如果某個山峰的高度小於區間的左端點,那麼就需要把這個山峰的高度修改為左端點的大小,這一定會是最小的代價。
  3. 如果某個山峰的高度大於區間的右端點,那麼就需要把這個山峰的高度修改為右端點的大小,這一定會是最小的代價。

  這是因為每個山峰是獨立的,每個山峰的修改都不會影響到其他的山峰。因此如果我們想讓總代價最小,只需要讓每個山峰的代價取到最小。

  這題的本質是找到一個區間,使得所有的點到這個區間的距離最小。

  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/