Try   HackMD

UVA 10041 Vito’s family | 解析 | CPE 1 星精選集

題目連結: https://onlinejudge.org/external/100/10041.pdf

平台:

題目解析

有一個黑幫老大 Vito 要去拜訪親戚,要找一間房子,離所有親戚的屋子距離最短。

Input

  • n
    :測資數量
  • r
    (0<r<500)
    :親戚數量
  • si
    (0<si<3000)
    :街道號碼

第一行會先有一數字

n 代表總共測資的數量。接著會有
n
行測資輸入,每筆測資的第一的數字
r
代表親戚的數量,接著會有
r
個數字
s0,s1,s2,...,sr1
代表每個親戚所在的街道號碼。

r
n s s s ... s
n s s s ... s
...
n s s s ... s

Output

找到老大家後,印出老大家到所有親戚家的最短路徑和。

  • distancei,j=|sisj|

解題思路

由於是給一組數字

S={s0,s1,s2,...,sr1},然後要找出一個數字
h
對於所有數字的差的和。所以可以整理成以下式子,
sum
就是我們最後要輸出的值。

sum=|hs0|+|hs1|+|hs2|+...+|hsr1|=i=0r1|hri|

找到

h 的方法最簡單就是利用中位數的概念。

中位數
給定一排序過的集合

S={s1,s2,s3,...,sn}
S
的中位數
mid
為:

  • n
    是偶數:
    mid=sn2
  • n
    是奇數:
    mid=(sn2+sn+12)/2

實作方式

讀入測資後用 qsort() 排序,找出中位數。並依序求到各點的距離和。

中位數簡化

若依照原本的敘述進行中位數的實作,程式碼如下:

// 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;
    }
}

但由於是求到各點距離總和,我們可以簡化一下:

even: size = 4
0   1   2   3   index
|---|---|---|
1   5   10  20  value

假設輸入測資為

S={s0,s1,s2,s3},其大小
size=|S|=4
,中位數
mid=(s1+s2)/2

到各點的最短距離和
sum

sum=|mids0|+|mids1|+|mids2|+|mids3|=(s3s0)+(s2s1)

我們可以發現在中位數對稱的兩項,可以合併在一起。

對所有

si, sj
i+j=size1

|midsi|+|midsj|=sjsi

就會如下圖所示(

mido 表示,| 表示
si
sj
):

even: size = 4
0   1   2   3   index
|---|---|---|
1   5   10  20  value
|-----o-----|
    |-o-|

可以看到,無論

mid
[si,sj]
的範圍內的哪個位置,成對的距離相加都不變。但為了要符合
S
中每組成對的距離,
mid
只能是
ssize/2
ss/21

也因此可以把中位數的程式簡化成以下:

int median(int *arr, int size){
    return arr[size/2];
}

效能

時間複雜度:

O(nlogn)

假設 qsort() 實作為

O(nlogn)

空間複雜度:

O(n)

程式碼

#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);
    }
}