(python3)
2108번: 통계학
첫째 줄에 수의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 단, N은 홀수이다. 그 다음 N개의 줄에는 정수들이 주어진다. 입력되는 정수의 절댓값은 4,000을 넘지 않는다.
www.acmicpc.net
아이디어 : 최빈값 구하기, key 파라미터로 커스텀한 기준으로 sort 하기
가장 직관적인 풀이로 접근했습니다.
평균값, 범위는 max, min, len 으로 쉽게 해결이 가능하고, median 도 sort 해서 가운데값을 찾아주면 됩니다.
최빈값이 문젠데, 최빈값은 여러개일 경우에 두번째로 작은 값을 찾아야하는 커스텀한 기준이 있으므로 이걸 잘 반영해줘야합니다. 제일 쉬운 방법은 각 수와 카운트값을 저장해서 카운트값 기준 내림차순, 수 기준 오름차순으로 정렬해주고, 최빈값이 2개 이상이면 두번째 값을 찾아주는 것입니다.
파이썬에서 제공하는 기본 sort, sorted 메서드에서 사용되는 방식이 timsort 이기 때문에 시간복잡도는 O(NlogN) 이 됩니다.
key 파라미터로 커스텀한 기준으로 sort 하는 방법은 아래 글에 작성해뒀습니다.
[sort] 파이썬에서 커스텀한 기준으로 이터러블한 객체 정렬하기
key 파이썬에서 제공하는 sorted 기능은 key 파라미터로 커스텀한 기준을 부여하여 정렬할 수 있습니다. 파이썬 sort 관련 독스에서도 이를 확인할 수 있습니다. https://docs.python.org/ko/3/howto/sorting.html#k
jamong-5.tistory.com
코드
def solution(input) :
# 입력받기
N = int(input().strip())
nlist = []
for _ in range(N) :
nlist.append(int(input().strip()))
mean, median, most_custom, tigerup = 0,0,0,0 # 변수 설정
nlist.sort()
mean = round(sum(nlist) / N)
median = nlist[N//2]
# 커스텀 최빈값 구하기
cnt = 1
num = nlist[0]
most = [] # nlist 배열을 (값, 횟수)] 의 배열로 표현을 치환 = most
for i in nlist[1:] :
if num != i :
most.append((num,cnt))
num = i
cnt = 1
else :
cnt+=1
most.append((num,cnt))
most.sort(key = lambda x : (x[1]*-1,x[0])) # 횟수 기준 내림차순, 값 기준 오름차순으로 정렬
# 최빈값이 여러개일대 두 번째로 작은 값 출력
if len(most) > 1 and most[0][1] == most[1][1] :
most_custom = most[1][0]
else : most_custom = most[0][0]
# 범위 계산
tigerup = max(nlist) - min(nlist)
print(mean)
print(median)
print(most_custom)
print(tigerup)
solution(input)
최단 시간 코드 분석
이번에는 해당 문제의 최단 시간 코드도 한번 분석해보려고 합니다.
제 코드는 484ms, 공개된 코드 중 최단 시간 코드는 176ms 입니다. 세상엔 역시 똑똑한 사람이 많고 그만큼 배울것도 많네요.
참고한 코드 출처 : https://www.acmicpc.net/source/37033270
이 코드의 경우 sort 를 한번도 사용하지 않고도 sort 된 형태를 만드는 방식으로 구현했습니다. 배열도 딱 한번 돌면서 모든 요소들을 계산하면서 시간이 또 단축되네요. 자세한 사항은 줄마다 주석으로 넣었습니다. O(N) 으로 해결됩니다.
from sys import stdin
def sol2108():
n = int(stdin.readline())
counts = [0]*8001 # 입력되는 정수가 -4000~4000 으로 주어지므로 각 정수의 count 값을 저장하는 배열로 치환
s = 0 # sum
for i in stdin:
num = int(i)
counts[num+4000] += 1
maxc = max(counts) # 최빈값의 횟수
mode = mcnt = 0 # 최빈값, 몇번째 최빈값인지
idx = 0
med = None
mi, ma = 4001, -4001
for i in range(8001): # 이렇게 탐색하면 가장 작은 수 부터 오름차순으로 탐색하는 꼴
cnt = counts[i]
if cnt == 0: # 등장한적 없는 수 면 넘어가기
continue
num = i-4000 # 실제 값으로 변경
s += num*counts[i] # sum 에 더하기
if cnt == maxc and mcnt < 2: # 최빈값 중 하나고, 최빈값이 1개면 1번째, 2개 이상이면 2번째로 작은 최빈값으로 저장됨.
mode = num
mcnt += 1
mi = min(mi, num) # 최소
ma = max(ma, num) # 최대
# 여기부턴 중앙값 구하기. 중앙값은 정렬된 수열에서 인덱스가 정해져있음.
# 지금 데이터가 저장된 방식은 애초에 오름차순 정렬되어 있다고 보면 됨.
# 이 경우는 idx가 배열의 인덱스가 아니라 몇번째 숫자인지를 세어야하기 떄문에 1부터 시작
# 고로 중앙값 idx = N//2 + 1 이 됨.
idx += counts[i]
if idx >= n//2+1 and med == None: # 중앙값이 되는 수가 여러번 등장하는 경우 idx 가 딱 중앙값에서 끝나지 않을 수 있음. 그래서 크거나 같다로 처리. 처음으로 중앙값 인덱스를 넘어가는 순간의 값이 우리가 찾는 중앙값임.
med = num
print(round(s/n), med, mode, ma-mi, sep='\n') # sep 파라미터를 사용해서 한번에 출력
if __name__ == '__main__':
sol2108()
my solved.ac :
solved.ac
알고리즘 문제해결 학습의 이정표 🚩 Baekjoon Online Judge 문제들의 난이도 및 티어 정보를 제공하는 사이트입니다.
solved.ac
'Algorithem > 백준 PS with code' 카테고리의 다른 글
백준 #17779 - [G3] 게리맨더링 2 : 구현, 브루트포스 (0) | 2023.05.30 |
---|---|
백준 #1205 - [S4] 등수 구하기 : 구현 (0) | 2023.05.29 |
백준 #1806 - [G4] 부분합 : 두 포인터 (0) | 2023.05.27 |
백준 #14503 - [G5] 로봇 청소기 : 구현 (1) | 2023.05.25 |
백준 #15685 - [G4] 드래곤 커브 : 구현 (0) | 2023.05.24 |