백준 2108. 통계학
정렬 관련 문제
정렬보다는 구현이 중요한 문제였다
1. 간단 설명
4개의 평균을 구해야한다
- 산술평균(Arithmetic mean) : N개의 수들의 합을 N으로 나눈 값
- 중앙값(Median) : N개의 수들을 증가하는 순서로 나열했을 경우 그 중앙에 위치하는 값
- 최빈값(Mode) : N개의 수들 중 가장 많이 나타나는 값
- 범위(Range) : N개의 수들 중 최댓값과 최솟값의 차이
2. 예시
5 1 3 8 -2 2
- 산술 평균 :
(1 + 3 + 8 + -2 + 2) / 5 = 2.4
인데 소수 첫째자리에서 반올림한다 답은 2 - 중앙값 : 수를 오름차순으로 정렬시, 중앙에 위치하는 값은 3
- 최빈값 : 모두 한번씩 등장하므로 여기서는 두번째로 작은값인 1을 출력
- 범위 : 최소값은 -2, 최대값은 8이므로 10의 범위를 가짐
주의사항
- 산술 평균은 소수점 첫째 자리에서 반올림 할 것
- N의 값은 홀수이므로, 중앙값은 하나만 존재한다
- 최빈값이 여러개인 경우, 두번째로 작은 값을 출력할 것
- 범위는 양수임
특히 예제 4번에서
3 0 0 -1
(0 + 0 + (-1)) / 3 = -0.333333...
이고 이를 첫째 자리에서 반올림하면0
이다.-0
으로 출력하면 안된다.
라고 명시되어있다. 부동소수점의 특성을 잘 생각해 볼 것
3. 알고리즘
간단히 생각나는 대로 풀이한 방법
1
2
3
4
5
6
7
8
9
10
11
12
1. N을 입력받고 각 수를 입력받는다
1-1. 수를 입력받으면서, 최빈값 체크를 위해 각 수가 몇번 나왔는지 기록하면서 저장한다.
2. 배열을 저장한다 (중앙값 계산을 위해)
3. 산술 평균을 구해서 출력한다
- 저장된 원소의 합을 구해서, N으로 나눠서 출력한다
4. 중앙값을 출력한다
- 간단히 v[N/2]로 출력 가능
5. 최빈값 구하기
- 먼저 1-1에서 체크한 배열을 체크하여 가장 많이 나온 수를 확인한다
- 이후 이 수와 같은 회수 만큼 나온 수가 있는지 체크한 뒤, 두번쨰로 작은 값을 출력한다
6. 범위를 구해서 출력한다
- v[N-1]에서 v[0]를 빼서 절대값을 취해서 출력한다
4. 소스코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>
#include <vector>
#include <cmath>
#include <numeric>
#include <algorithm>
using namespace std;
#define SIZE 8001
#define OFFSET 4000 // [-4000 : 0] ~ [4000 : 8000]으로 저장
int N;
int freq[SIZE];
vector<int> numbers;
void Input() {
freopen_s(new FILE*, "input.txt", "r", stdin);
cin >> N;
for (int i = 0; i < N; i++) {
int tmp; cin >> tmp;
freq[tmp + OFFSET]++; // 입력값은 -4000 ~ 4000
numbers.push_back(tmp);
}
sort(numbers.begin(), numbers.end(), less<int>()); // 오름차순 정렬
}
int main() {
Input();
// 1. 산술평균
double mean = round(accumulate(numbers.begin(), numbers.end(), 0) / double(N));
if (mean == 0) // 부동소수점 특성상 음의 0이 나올 경우 절대값으로 음수 부호를 떼준다
cout << abs(mean) << '\n';
else
cout << mean << '\n';
// 2. 중앙값
cout << numbers[N / 2] << '\n';
// 3. 최빈값
int max_freq = 0;
vector<int> mode_candidates;
for (int i = 0; i < SIZE; i++) {
if (freq[i] > max_freq) {
max_freq = freq[i];
mode_candidates = { i }; // 기존의 값을 버리고 새로운 값으로 업데이트
}
else if (freq[i] == max_freq) { // 같은 빈도의 최대값이 등장한 경우 넣음
mode_candidates.push_back(i);
}
}
// 2개 이상이면 두번째 원소를, 하나면 첫번째 원소를 출력
int mode = mode_candidates.size() > 1 ? mode_candidates[1] : mode_candidates[0];
cout << mode - OFFSET << '\n';
// 4. 범위
cout << abs(numbers[N - 1] - numbers[0]);
return 0;
}
간단해 보이지만, 은근 꼼꼼히 신경쓸게 있는 문제였다
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.