본문 바로가기

algorithm/백준알고리즘

[백준알고리즘] 2108번: 통계학 -C++

728x90

[백준알고리즘] 2108번: 통계학 -C++

2108번: 통계학 (acmicpc.net)

 

2108번: 통계학

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

www.acmicpc.net

그렇게 어렵게 풀 문제는 아니다.

 

실제로 구하라고 하는 값들을 계산하면 된다.

 

산술평균

산술평균을 구하기 위해서 입력 들어오는 모든 값을 우선 더해주었다. 이 더한 값을 입력이 들어오는 개수인 \(n\)으로 나눠주면 된다.

 

여기서 입력 값들의 합은 int의 범위를 넘어서기 때문에 int64_t (double)로 설정해주어서 오버플로우를 방지했다.

 

중앙값

중앙값을 구해주기 위해서는 우선 모든 수를 입력받은 뒤에, 각 수가 몇 번 호출되었나 차례대로 확인해본다. \(n\) 개의 수가 입력됐을 때 \(n/2\) 번째 수가 중앙값이 되기 때문에 카운팅 하던 횟수가 \(n/2\) 보다 커지는 경우가 중앙값이 될 것이다. (카운트는 \(1\)부터 시작하며 \(n\)은 홀수이기 때문에 같은 경우는 해당 안됨)

 

최빈값

최빈값도 마찬가지로 모든 수를 입력 받은 뒤에 각 수가 얼마나 입력됐는가를 확인한다. 여기서는 최빈값이 여러 개일 경우에는 두 번째로 작은 값을 선택하라고 했다.

사실 여기서 두번째로 작은 값이라고 하길래 {가장 작은 값, 두 번째로 작은 값} 이렇게 확인하면 될 줄 알았는데, 첫 번째 예시를 보니 {가장 작은 값, 첫 번째로 작은 값, 두 번째로 작은 값}이었다.

 

아무튼 그래서 나는 크기가 4인 배열을 정해두고, {카운팅 횟수, 해당 카운팅의 가장 작은 값, 해당 카운팅의 첫 번째로 작은 값, 해당 카운팅의 두 번째로 작은 값}으로 배열을 채워넣었다.

 

범위

범위의 경우에는 입력을 받을 때 최댓값과 최솟값을 분류해놓았다.

 


2022.02.28

새롭게 문제를 풀었다. 예전에 푼 코드라고 해서 다시 돌려봤는데 컴파일 에러가 발생한다.

그래서 아래 예전 코드는 놔둔 채로 새로 통과한 코드를 업로드한다.

 

#include <cstdio>
#include <cmath>
#include <set>

void solve(void);

int main(void) {
	solve();

	return 0;
}

void solve(void) {
	/* 입력 */
	unsigned int uiNum = 0;
	scanf("%u", &uiNum);

	const unsigned int ARR_SIZE = 8001 + 1;
	unsigned int arrNum[ARR_SIZE] = { 0, };
	for ( unsigned int uiCnt = 0; uiCnt < uiNum; ++uiCnt ) {
		int iVal = 0;
		scanf("%d", &iVal);

		arrNum[iVal + 4000]++;
	}

	/* 통계학 값 구하기 */
	const int VAL_MIN = -4000;
	const int VAL_MAX = 4000;
	const int VAL_INIT = 4001;
	const int MID_IDX = uiNum / 2;

	int iSum = 0;					//< 합
	int iMedian = VAL_INIT;			//< 중앙값
	int iModeSmaller = VAL_INIT;	//< 최빈값2
	int iMode = VAL_INIT;			//< 최빈값1
	int iMin = VAL_MAX;				//< 최솟값
	int iMax = VAL_MIN;				//< 최댓값
	int iCnt = 0;					//< 카운트

	for ( unsigned int uiIdx = 0; uiIdx < ARR_SIZE; ++uiIdx ) {
		if ( 0 == arrNum[uiIdx] ) {
			continue;
		}

		const int idx = uiIdx - 4000;

		// 합
		iSum += (idx * arrNum[uiIdx]);
		
		// 중앙값
		if ( MID_IDX < iCnt ) {
			// nothing
		}
		else if ( MID_IDX <= (iCnt = iCnt + arrNum[uiIdx]) ) {
			iMedian = idx;
		}

		// 최빈값
		if ( arrNum[iModeSmaller + 4000] < arrNum[uiIdx] ) {
			iMode = idx;
			iModeSmaller = idx;
		}
		else if ( arrNum[iModeSmaller + 4000] == arrNum[uiIdx] && iMode == iModeSmaller ) {
			iModeSmaller = idx;
		}

		// 범위
		iMin = (iMin < idx) ? iMin : idx;
		iMax = (idx < iMax) ? iMax : idx;
	}

	/* 출력 */
	printf("%.0lf\n%d\n%d\n%d", 
		::round(1.0 * iSum / uiNum) + 0.0, 
		iMedian, 
		iModeSmaller, 
		iMax - iMin);
}

 

아래는 위의 방법대로 풀이한 방식이다.

더보기
#include <iostream>
#include <climits>
#include <cmath>

void solve(void);

int main(void)
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(NULL);

	solve();
}

void solve(void)
{
	int n;
	std::cin >> n;

	int64_t total = 0;
	int max_val = INT_MIN;
	int min_val = INT_MAX;
	int count[8001] = { 0, };
	for (int i = 0; i < n; i++)
	{
		int temp;
		std::cin >> temp;

		count[temp + 4000]++;
		total += temp;
		max_val = max_val < temp ? temp : max_val;
		min_val = min_val < temp ? min_val : temp;
	}

	int total_count = 0;
	int mid_val = INT_MIN;
	int max_count[4]; // count, smallest, second, third
	std::fill_n(max_count, 4, INT_MIN);
	for (int i = 0; i <= 8001; i++)
	{
		if (!count[i]) continue;

		int curr = i - 4000;
		total_count += count[i];
		if (n / 2 < total_count && INT_MIN == mid_val)
			mid_val = curr;

		if (max_count[0] == count[i])
			if (INT_MIN == max_count[2])
				max_count[2] = curr;
			else if (INT_MIN == max_count[3])
				max_count[3] = curr;

		if (max_count[0] < count[i])
		{
			max_count[0] = count[i];
			max_count[1] = curr;
			max_count[2] = INT_MIN;
			max_count[3] = INT_MIN;
		}
	}

	std::cout << std::round(total * 1.0 / n) << std::endl;
	std::cout << mid_val << std::endl;
	std::cout << (INT_MIN == max_count[2] ? max_count[1] : max_count[2]) << std::endl;
	std::cout << max_val - min_val;
}

 

잘못된 점이나 부족한 점 지적해주시면 감사하겠습니다

728x90