프로그래머스 위클리 챌린지 2주차
TL;DR
- 배열(리스트, List)
- 구현(Implementation)
문제 요약
1. 학교의 과제에서 각 학생들이 받은 점수의 평균을 사용해서 각 학생이 받은 학점을 반환하는 함수를 작성한다.
2. 각 학생들은 자기 자신과 다른 학생들의 점수를 준다.
3. 각 학생들의 점수에서 자기 자신이 준 점수가 받은 점수 중 유일한 최고점이거나 최저점일 경우 해당 점수를 제외하고 평균을 계산한다.
4. 평균이 90점 이상인 학생은 A, 80점 이상 90점 미만인 학생은 B, 70점 이상 80점 미만인 학생은 C, 50점 이상 70점 미만인 학생은 D, 50점 미만인 학생은 F를 부여한다.
- 학생들은 자기 자신 및 다른 학생들에게 점수를 부여한다.
- 한 학생이 받은 점수 중 자기 자신이 준 점수가 받은 점수 중에 유일한 최고점인지 최저점인지 확인한다.
- 만약 해당한다면 해당 점수를 제외하고 평균을 구한다. 해당하지 않는다면 모두 포함하여 평균을 구한다.
- 구해진 평균을 문제에서 제시된 기준에 따라서 학점을 측정한다.
입출력 형태
주어진 입력 scores의 각 원소는 각 학생이 다른 학생들에게 준 점수를 나타낸다.
- 가장 첫 번째 원소인 [100, 90, 98, 88, 65]는 0번째 학생이 0번, 1번, 2번, 3번, 4번 학생들에게 준 점수를 나타낸다.
각 학생들이 받은 점수는 행이 아닌 열로 확인했을 때의 점수이다.
- 0번째 학생이 받은 점수는 1열에 해당하는 [100, 50, 47, 61, 24]가 된다.
- 0번째 학생이 받은 점수 중 100점은 자기 자신이 준 점수이고, 받은 점수 중 유일한 최고점이기 때문에 이를 제외한 [50, 47, 61, 24]의 평균을 구한다. 그렇게 될 경우 이 학생은 F학점을 받는다.
- 4번째 학생이 받은 점수는 4열에 해당하는 [65, 77, 67, 65, 65]가 된다.
- 4번째 학생이 받은점수 중 65점은 자기 자신이 준 점수이다. 그리고 받은 점수 중 최저점이다. 하지만 다른 학생들도 동일한 65점을 부여하였기 때문에 제외하지 않고 평균에 포함하여 계산한다. 그렇게 될 경우 이 학생은 D를 받는다.
풀이
문제에서 주어지는 입력은 학생들이 준 점수이고, 계산에 필요한 것은 받은 점수이기 때문에 주어진 배열의 행과 열을 바꾸어 생각해서 문제를 풀어야 한다.
from typing import List
def solution(scores: List[int]) -> str:
for column in range(len(scores[0])): # column이 각 학생의 번호를 나타냄
student_score = []
_max, _min = -1, 101
max_count, min_count = 0, 0
for row in range(len(scores)):
student_score.append(scores[row][column])
_max = max(_max, scores[row][column])
_min = min(_min, scores[row][column])
if _max == student_score[column]: # 자기 자신에게 준 점수가 최고점일때
for score in student_score:
if score == _max:
max_count += 1
if max_count == 1:
del student_score[column]
elif _min == student_score[column]: # 자기 자신에게 준 점수가 최저점일때
for score in student_score:
if score == _min:
min_count += 1
if min_count == 1:
del student_score[column]
mean = sum(student_score) / len(student_score)
if mean >= 90:
answer += 'A'
elif mean >= 80:
answer += 'B'
elif mean >= 70:
answer += 'C'
elif mean >= 50:
answer += 'D'
else:
answer += 'F'
return answer
처음 문제를 해결할 때 작성한 코드이다. 행과 열을 반대로 2중 반복문을 사용하여 각 학생이 받은 점수를 `student_score`에 저장하였다.
각 학생들이 받은 점수인 `student_score`에서 최대값과 최소값을 찾고, 반복문을 순회하며 최대값과 최소값의 등장 횟수를 찾았다.
이후 각 학생들이 준 자기 자신에 점수에 해당하는, 즉 column 인덱스의 값을 최대값과 최소값에 대해서 비교하고, 각 값이 유일할 경우에는 제외하고 평균을 계산하도록 작성하였다. 이후 측정한 평균에 대해서는 각 기준에 따라서 학점이 측정된다.
다른 풀이
위에서 작성한 풀이는 직관적으로는 보이지만 불필요한 연산들이 많이 수행된다. 각 학생들이 받은 점수를 새롭게 저장하기 위한 리스트를 선언하고, 새로 선언한 리스트에서 또 반복문을 통해 최대값과 최소값이 등장한 횟수를 측정한다.
이 코드를 개선한 코드는 아래와 같다.
import collections
from typing import List
def solution(scores: List[int]) -> str:
def rank(mean_score: int) -> str:
if mean_score >= 90:
return 'A'
elif mean_score >= 80:
return 'B'
elif mean_score >= 70:
return 'C'
elif mean_score >= 50:
return 'D'
else:
return 'F'
answer = ''
scores = list(map(list, zip(*scores))) # 행열 변환, 각 원소가 자기 자신이 받은 점수를 나타냄
for i, score in enumerate(scores): # score[i] : 자기 자신에게 준 점수
counter = collections.Counter(score)
_max, _min = max(score), min(score)
if (score[i] == _max and counter[_max] == 1) or (score[i] == _min and counter[_min] == 1):
del score[i]
mean = sum(score) / len(score)
answer += rank(mean)
return answer
우선 `zip`과 `*`를 사용하여 행열을 뒤집는 것을 한 줄로 해결하였다.
- `*`로 scores의 각 원소들을 언팩(unpack) 한 뒤 `zip`을 수행하면 행과 열이 바뀐, 즉 우리가 원하는 각 학생이 받은 점수들로 구성된 이중 리스트를 만들 수 있다.
이렇게 행열을 바꾼 배열에서 배열의 원소들의 개수를 딕셔너리 형태로 반환해주는 Counter를 사용하여 배열 내 최대값과 최소값의 점수를 파악하였다. 그리고 각 경우에 따라서 제거할 점수를 제거하고 평균을 구할 수 있도록 하였다.
학점을 주는 구문은 함수로 따로 분리하여 작성하였다.
같은 기능을 하는 코드이지만 훨씬 간결하게 잘 정리되었다. 하고나서 뿌듯했다.
'개발 > Algorithm' 카테고리의 다른 글
[리트코드] Balanced Binary Tree(python) (0) | 2021.08.22 |
---|---|
[리트코드] Serialize and Deserialize Binary Tree(python) (0) | 2021.08.20 |
[백준] 그대, 그머가 되어(python) (0) | 2021.08.13 |
[백준] 특정 거리의 도시 찾기(python) (0) | 2021.08.05 |
[리트코드] Network Delay Time(python) (0) | 2021.08.01 |