--- title: UVA 10041 Vito’s family description: CPE 1 星精選題目(CPE10406, UVA10041),AC 解答與題目解析 image: tags: - 解題筆記 - UVA - CPE - 程式競賽 --- # UVA 10041 Vito’s family | 解析 | CPE 1 星精選集 **題目連結:** https://onlinejudge.org/external/100/10041.pdf **平台:** - [Online Judge](https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&category=0&problem=982&mosmsg=Submission+received+with+ID+29374919) - [Zero Judge](https://zerojudge.tw/ShowProblem?problemid=a737) ## 題目解析 有一個黑幫老大 Vito 要去拜訪親戚,要找一間房子,離所有親戚的屋子距離最短。 ### Input - $n$:測資數量 - $r$ $(0<r<500)$:親戚數量 - $s_i$ $(0<s_i<3000)$:街道號碼 第一行會先有一數字 $n$ 代表總共測資的數量。接著會有 $n$ 行測資輸入,每筆測資的第一的數字 $r$ 代表親戚的數量,接著會有 $r$ 個數字 $s_0, s_1,s_2,...,s_{r-1}$ 代表每個親戚所在的街道號碼。 ```text r n s s s ... s n s s s ... s ... n s s s ... s ``` ### Output 找到老大家後,印出老大家到所有親戚家的最短路徑和。 - $distance_{i,j} = |s_i - s_j|$ ## 解題思路 由於是給一組數字 $S = \{s_0,s_1,s_2,...,s_{r-1}\}$,然後要找出一個數字 $h$ 對於所有數字的差的和。所以可以整理成以下式子,$sum$ 就是我們最後要輸出的值。 \begin{aligned} sum &= |h-s_0|+|h-s_1|+|h-s_2|+...+|h-s_{r-1}| \\ &=\sum_{i=0}^{r-1}|h-r_i| \\ \end{aligned} 找到 $h$ 的方法最簡單就是利用中位數的概念。 ::: info <i class="fa fa-info-circle" aria-hidden="true"></i> **中位數** 給定一排序過的集合 $S = \{s_1,s_2,s_3,...,s_n\}$,$S$ 的中位數 $mid$ 為: - 若 $n$ 是偶數:$mid = s_{\frac{n}{2}}$ - 若 $n$ 是奇數:$mid = (s_{\frac{n}{2}} + s_{\frac{n+1}{2}})/2$ ::: ## 實作方式 讀入測資後用 `qsort()` 排序,找出中位數。並依序求到各點的距離和。 ### 中位數簡化 若依照原本的敘述進行中位數的實作,程式碼如下: ```c // odd: size = 5 // 0 1 2 3 4 index // |---|---|---|---| // mid = arr[size/2] // // even: size = 6 // 0 1 2 3 4 5 index // |---|---|---|---|---| // mid = (arr[size/2] + arr[size/2-1]) / 2 int median(int *arr, int size){ if(size % 2){ // odd number return arr[size/2]; }else{ // even number return (arr[size/2] + arr[size/2-1]) / 2; } } ``` 但由於是求到各點距離總和,我們可以簡化一下: ```text even: size = 4 0 1 2 3 index |---|---|---| 1 5 10 20 value ``` 假設輸入測資為 $S = \{s_0,s_1,s_2,s_3\}$,其大小 $size = |S| = 4$,中位數 $mid = (s_1+s_2)/2$。 到各點的最短距離和 $sum$: \begin{aligned} sum &= |mid-s_0|+|mid-s_1|+|mid-s_2|+|mid-s_3| \\ &=(s_3-s_0)+(s_2-s_1) \end{aligned} 我們可以發現在中位數對稱的兩項,可以合併在一起。 對所有 $s_i,\ s_j$ 且 $i+j=size-1$ $$ |mid-s_i|+|mid-s_j| = s_j-s_i $$ 就會如下圖所示($mid$ 用 `o` 表示,`|` 表示 $s_i$、$s_j$): ```text even: size = 4 0 1 2 3 index |---|---|---| 1 5 10 20 value |-----o-----| |-o-| ``` 可以看到,無論 $mid$ 在 $[s_i, s_j]$ 的範圍內的哪個位置,成對的距離相加都不變。但為了要符合 $S$ 中每組成對的距離,$mid$ 只能是 $s_{size/2}$ 或 $s_{s/2-1}$。 也因此可以把中位數的程式簡化成以下: ```c int median(int *arr, int size){ return arr[size/2]; } ``` ## 效能 時間複雜度: $O(n \log n)$ > 假設 `qsort()` 實作為 $O(n \log n)$ 空間複雜度: $O(n)$ ## 程式碼 ```c #include <stdio.h> #include <stdlib.h> int cmp(const void *a, const void *b) { return *(int *)a - *(int *)b; } int main() { int n; scanf("%d", &n); while (n--) { int r; scanf("%d", &r); int street[r]; for (int i = 0; i < r; i++) { scanf("%d", &street[i]); } qsort(street, r, sizeof(int), cmp); int sum = 0; for (int i = 0; i < r; i++) { sum += abs(street[r/2] - street[i]); } printf("%d\n", sum); } } ```