본문 바로가기
코딩테스트 준비/백준

[백준/BOJ][C++] 2108번 통계학

by 스테디코디스트 2023. 9. 6.
반응형

<문제 소개>


<소스 코드>

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
	// 쓰레드 환경이 아닐때 버퍼를 분리하여 처리속도를 빠르게 해줌
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	int N;
	cin >> N;

	vector<int> v;
	map<int, int> m; // < 수, 빈도수 >

	int sum = 0; // 합계

	for (int i = 0; i < N; i++)
	{
		int x;
		cin >> x;

		sum += x; // 각 수들의 합을 저장
		v.push_back(x);
	
		// 처음 나온 수인 경우에는 1로 초기화 나왔던 경우에는 1씩 증가
		if (!m[x]) m[x] = 1;
		else m[x]++;
	}
	
	// 원소 정렬
	sort(v.begin(), v.end());
	int size = v.size(); // 벡터의 크기를 int형으로 저장(size_t는 unsigned int형이라서 음수값을 가질 수 없음)
	int avg = 0; // 산술평균

	// 반올림
	// 10을 곱해서 나눈 값을 10으로 나눈 나머지로 소수점 첫째자리의 수를 구함
	if (abs(((sum * 10) / size) % 10) >= 5)
	{
		// 올림
		if (sum >= 0)
		{
			// 양수
			avg = sum / size + 1;
		}
		else
		{
			// 음수
			avg = sum / size - 1;
		}
	}
	else
	{
		// 내림
		avg = sum / size;
	}

	int mid = v[v.size() / 2]; // 중앙값
	int range = v[v.size() - 1] - v[0]; // 범위

	int freq = 0; // 최빈값
	int freqVal = 0; // 빈도수

	for (map<int, int>::iterator iter = m.begin(); iter != m.end(); iter++)
	{
		// 가장 많은 빈도수를 찾음
		freqVal = max(freqVal, iter->second);
	}

	int count = 0;

	for (map<int, int>::iterator iter = m.begin(); iter != m.end(); iter++)
	{
		if (iter->second == freqVal)
		{
			// 최빈값 저장
			freq = iter->first;
			count++;

			// 최빈값이 여러개인 경우
			if (count == 2)
			{
				freq = iter->first;
				break;
			}
		}
	}

	cout << avg << "\n";
	cout << mid << "\n";
	cout << freq << "\n";
	cout << range << "\n";

	return 0;
}

<소스코드 2> - 깔끔하게 정리한 코드

#include <iostream>
#include <math.h>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
	// 쓰레드 환경이 아닐때 버퍼를 분리하여 처리속도를 빠르게 해줌
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	int N;
	cin >> N;

	vector<int> list; // 원소들을 담음
	vector<int> frequency(8001); // -4000 ~ 4000 범위내의 숫자 ... 빈도수를 셈

	int maxFreq = 0; // 가장 큰 빈도수
	int sum = 0; // 합계

	for (int i = 0; i < N; i++)
	{
		int x;
		cin >> x;

		// 음수 인덱스가 올 수 없으므로 4000씩 증가시켜서 인덱스를 연결시켜준다.
		// ex) v[-4000] => v[0], v[4000] => v[8000]
		frequency[x + 4000]++; // 각 숫자별 빈도수를 측정

		// 현재 숫자의 빈도수가 가장 큰 빈도수인지 확인
		maxFreq = max(frequency[x + 4000], maxFreq);

		sum += x; // 합계를 구함
		list.push_back(x); // 원소 추가
	}

	// 원소 정렬
	sort(list.begin(), list.end());

	int avg = round((float)sum / N); // round : 반올림 함수 .. 산술평균
	int mid = list[N / 2]; // 중앙값
	int freq = 0; // 최빈값
	int range = list[N - 1] - list[0]; // 범위

	// 최빈값 구하기
	bool checkFirst = false; // 첫번째 값이 나왔는지 체크하는 변수

	for (int i = 0; i < frequency.size(); i++)
	{
		int curFreq = frequency[i]; // i의 빈도수

		// 현재 값이 가장 큰 빈도수를 가지는 수들 중 하나인 경우
		if (curFreq == maxFreq)
		{
			// 두번째 숫자를 찾은 경우
			if (checkFirst)
			{
				freq = i - 4000;
				break;
			}

			// 첫번째 숫자를 찾은 경우
			freq = i - 4000;
			checkFirst = true;
		}
	}

	cout << avg << "\n";
	cout << mid << "\n";
	cout << freq << "\n";
	cout << range << "\n";

	return 0;
}

<풀이과정>

1. N을 입력받음

2. 원소들을 저장할 벡터 v와 각 원소와 빈도수를 각각 키와 값으로 가지는 맵 m을 선언

3. 합계를 나타내는 sum을 0으로 선언하고, N만큼 반복하면서 x를 입력받고, sum에 x를 매번 더해준다.

4. 또한 벡터 v에도 x를 넣어주고, 맵 m에도 x를 넣어주는데 처음 넣는 경우에는 1로 초기화시키고, 같은 수가 또 나온 경우에는 해당 값을 1씩 증가시켜준다.

5. sort를 이용해 벡터 v를 정렬시켜준다.

6. 벡터의 크기 v.size()는 size_t 형이라 unsigned int형으로 취급되기 때문에 나눗셈할 때 음수 계산이 되지않아 int형 size로 형 변환을 시켜주었다.

7. 그리고 산술평균을 저장할 avg 변수를 선언하고 위에서 구한 합계인 sum을 이용해 구한다.

8. sum을 그냥 size로 나누어버리면 소수점을 구할 수 없기에 sum에 10을 곱해서 size로 나누어 소수점 첫째자리를 일의자리에 나타낸 뒤 해당 값을 10으로 나눈 나머지를 구하면 일의자리만 추출할 수 있고, 해당 값이 5이상인지 확인해 반올림 연산을 진행한다.

9. 5이상의 값인 경우 올림을 진행하는데, 올림을 할 때 양수와 음수가 다르게 동작한다. sum을 size로 나누어 내림이 된 기본값에서 양수라면 1을 더해주고, 음수라면 1을 빼준다. 

10. 5이하의 값인 경우 내림을 진행한다. 내림은 sum을 size로 나눈 값을 그대로 사용하면 된다.

11. 다음으로 중앙값 mid는 sort로 정렬된 벡터의 중간 순서의 값을 넣어주면되고, 범위 range는 벡터의 마지막 값과 첫번째 값의 차를 구하여 넣어준다.

12. 마지막으로 최빈값 freq과 최빈값의 빈도수를 나타낼 freqVal를 선언해주고, 맵 m을 돌면서 가장많은 빈도수를 찾아 freqVal에 저장한다.

13. 최빈값이 여러개인 경우 두번째로 작은 값을 출력해야하기 때문에 순서를 셀 count 변수를 선언하고 다시한번 맵 m을 돌면서 앞서구한 freqVal와 같은 값을 가질 때의 키값을 freq에 저장하고, count를 1씩 증가시켜준다.

14. count가 2가 되는 경우는 최빈값이 여러개인 경우이므로 해당 값을 freq로 다시 저장해주고, break로 반복문을 빠져나간다.

15. 위의 과정이 모두 끝난 후 avg,mid,freq,range 순으로 출력해준다.

 

<두 번째 풀이>

- 이 풀이는 맵을 사용하지 않고 -4000부터 4000까지의 입력범위를 이용해 빈도수를 체크할 벡터를 만들었다.

- 반올림을 구할 때 round를 이용해 풀었다.(math.h 라이브러리를 가져오지 않으면 컴파일 오류가 발생했다.)


<코멘트>

두번째 최빈값과 산술평균 반올림이 있어서 난이도가 좀 있었던 문제였다.

다 풀고나서 산술평균은 round라는 함수가 있다는 것을 알았다ㅋㅋ

너무 지저분하게 푼 것 같아서 다른 분들의 풀이들을 참고해서 깔끔하게 정리하면서 다시 풀어보았다!


<제출결과>

- 처음 풀이

- 깔끔하게 정리한 풀이