[C] 2108번: 통계학

[C] 2108번: 통계학
2026-03-08

문제#

https://www.acmicpc.net/problem/2108

수를 처리하는 것은 통계학에서 상당히 중요한 일이다. 통계학에서 N개의 수를 대표하는 기본 통계값에는 다음과 같은 것들이 있다. 단, N은 홀수라고 가정하자.

  1. 산술평균 : N개의 수들의 합을 N으로 나눈 값
  2. 중앙값 : N개의 수들을 증가하는 순서로 나열했을 경우 그 중앙에 위치하는 값
  3. 최빈값 : N개의 수들 중 가장 많이 나타나는 값
  4. 범위 : N개의 수들 중 최댓값과 최솟값의 차이 N개의 수가 주어졌을 때, 네 가지 기본 통계값을 구하는 프로그램을 작성하시오.

입력#

첫째 줄에 수의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 단, N은 홀수이다. 그 다음 N개의 줄에는 정수들이 주어진다. 입력되는 정수의 절댓값은 4,000을 넘지 않는다.

출력#

첫째 줄에는 산술평균을 출력한다. 소수점 이하 첫째 자리에서 반올림한 값을 출력한다.

둘째 줄에는 중앙값을 출력한다.

셋째 줄에는 최빈값을 출력한다. 여러 개 있을 때에는 최빈값 중 두 번째로 작은 값을 출력한다.

넷째 줄에는 범위를 출력한다.

풀이#

이 문제는 정렬로도 풀 수 있고, 빈도 배열로도 풀 수 있습니다.

먼저 정렬을 사용하는 방법을 생각해 보면,

  • 중앙값은 정렬된 배열의 가운데 값
  • 범위는 최댓값 - 최솟값
  • 최빈값은 정렬된 배열을 순회하며 같은 값이 몇 번 나오는지 세어서 구할 수 있습니다

즉, 구현 자체는 직관적입니다.
하지만 입력 크기가 최대 500,000이므로 정렬을 사용하면 시간복잡도는

O(NlogN)O(N \log N)

이 됩니다.

그런데 이 문제는 중요한 특징이 하나 있습니다.
입력되는 정수의 범위가 -4000 ~ 4000으로 매우 작습니다.

즉, 가능한 값은 총

4000(4000)+1=80014000 - (-4000) + 1 = 8001

개뿐입니다.

이 경우 정렬 대신 빈도 배열을 사용하는 것이 더 효율적입니다.

배열 freq[8001]을 만들고, 값 xx + 4000 위치에 저장하면 각 숫자가 몇 번 등장했는지 바로 셀 수 있습니다.

예를 들면

  • -4000freq[0]
  • 0freq[4000]
  • 4000freq[8000]

처럼 대응됩니다.

이 방법을 사용하면 다음과 같이 구할 수 있습니다.

1. 산술평균#

입력받으면서 합을 구한 뒤, 마지막에 N으로 나누고 반올림하면 됩니다.

2. 중앙값#

빈도 배열을 앞에서부터 누적하면서 개수를 더해 갑니다.
누적 개수가 처음으로 N / 2를 넘는 위치가 중앙값입니다.

3. 최빈값#

먼저 가장 큰 빈도 max_freq를 찾습니다.
그다음 다시 앞에서부터 탐색하면서 max_freq와 같은 값을 찾습니다.

  • 첫 번째 값은 일단 저장
  • 두 번째 값이 나오면 그 값을 정답으로 사용

이렇게 하면 최빈값이 여러 개일 때 두 번째로 작은 값을 구할 수 있습니다.

4. 범위#

입력받는 동안 최솟값과 최댓값을 갱신해 두고, 마지막에 max - min을 출력하면 됩니다.

정리하면,

  • 정렬 풀이: O(N \log N)
  • 빈도 배열 풀이: O(N + 8001)

이고, 8001은 고정된 상수이므로 전체 시간복잡도는

O(N)O(N)

입니다.

따라서 이 문제는 정렬보다 빈도 배열을 사용하는 풀이가 더 적합합니다.

코드#

#include <math.h>
#include <stdio.h>
int main() {
int n;
scanf("%d", &n);
int freq[8001] = {0};
int sum = 0;
int min = 4001;
int max = -4001;
for (int i = 0; i < n; i++) {
int x;
scanf("%d", &x);
freq[x + 4000]++;
sum += x;
if (x < min)
min = x;
if (x > max)
max = x;
}
// 1. 산술평균
int avg = (int)round((double)sum / n);
// 2. 중앙값
int count = 0;
int mid = 0;
for (int i = 0; i < 8001; i++) {
count += freq[i];
if (count > n / 2) {
mid = i - 4000;
break;
}
}
// 3. 최빈값
int max_freq = 0;
for (int i = 0; i < 8001; i++) {
if (freq[i] > max_freq) {
max_freq = freq[i];
}
}
int mod = 0;
int found = 0;
for (int i = 0; i < 8001; i++) {
if (freq[i] == max_freq) {
mod = i - 4000;
found++;
if (found == 2) {
break;
}
}
}
// 4. 범위
int range = max - min;
printf("%d\n%d\n%d\n%d\n", avg, mid, mod, range);
return 0;
}