Programmers Coding Quiz #42 복서 정렬하기

김기욱·2021년 9월 9일
0

코딩테스트

목록 보기
59/68
post-custom-banner

문제 설명

복서 선수들의 몸무게 weights와, 복서 선수들의 전적을 나타내는 head2head가 매개변수로 주어집니다. 복서 선수들의 번호를 다음과 같은 순서로 정렬한 후 return 하도록 solution 함수를 완성해주세요.

전체 승률이 높은 복서의 번호가 앞쪽으로 갑니다. 아직 다른 복서랑 붙어본 적이 없는 복서의 승률은 0%로 취급합니다. 승률이 동일한 복서의 번호들 중에서는 자신보다 몸무게가 무거운 복서를 이긴 횟수가 많은 복서의 번호가 앞쪽으로 갑니다. 자신보다 무거운 복서를 이긴 횟수까지 동일한 복서의 번호들 중에서는 자기 몸무게가 무거운 복서의 번호가 앞쪽으로 갑니다. 자기 몸무게까지 동일한 복서의 번호들 중에서는 작은 번호가 앞쪽으로 갑니다.

제한사항

  • weights의 길이는 2 이상 1,000 이하입니다.
  • weights의 모든 값은 45 이상 150 이하의 정수입니다.
  • weights[i] 는 i+1번 복서의 몸무게(kg)를 의미합니다.
  • head2head의 길이는 weights의 길이와 같습니다.
  • head2head의 모든 문자열은 길이가 weights의 길이와 동일하며, 'N', 'W', 'L'로 이루어진 문자열입니다.
  • head2head[i] 는 i+1번 복서의 전적을 의미하며, head2head[i][j]는 i+1번 복서와 j+1번 복서의 매치 결과를 의미합니다.

    'N' (None)은 두 복서가 아직 붙어본 적이 없음을 의미합니다.
    'W' (Win)는 i+1번 복서가 j+1번 복서를 이겼음을 의미합니다.
    'L' (Lose)는 i+1번 복사가 j+1번 복서에게 졌음을 의미합니다.

  • 임의의 i에 대해서 head2head[i][i] 는 항상 'N'입니다. 자기 자신과 싸울 수는 없기 때문입니다.
  • 임의의 i, j에 대해서 head2head[i][j] = 'W' 이면, head2head[j][i] = 'L'입니다.
  • 임의의 i, j에 대해서 head2head[i][j] = 'L' 이면, head2head[j][i] = 'W'입니다.
  • 임의의 i, j에 대해서 head2head[i][j] = 'N' 이면, head2head[j][i] = 'N'입니다.

입출력 예제

weightshead2headresult
[50,82,75,120]["NLWL","WNLL","LWNW","WWLN"][3,4,1,2]
[145,92,86]["NLW","WNL","LWN"][2,3,1]
[60,70,60]["NNN","NNN","NNN"][2,1,3]

풀이

def solution(weights, head2head):
    counter = 1
    info = []
    for x,y in zip(weights, head2head):
        temp = {
            'no': counter,
            'weight': x,
            'win_rate': y.count('W')/(len(y)-y.count('N')) if len(y) > y.count('N') else 0,
            'h_point': len([weights[i] for i, v in enumerate(y) if v == 'W' and weights[i] > x])
        }
        info.append(temp)
        counter += 1

    info = sorted(info, key=lambda x: (-x['win_rate'], -x['h_point'], -x['weight'], x['no']))

    return [v['no'] for v in info]

주어진 조건에 따라 파이썬 모듈을 활용해 차근차근 정렬만 하면되는 문제입니다.
문제의 정렬 조건을 정리해보자면 다음과 같습니다.

Step1. 승률(이긴횟수/총시합전적 !주의: N으로 표기된 경우는 총시합전적에 들어가면 안됩니다)`
Step2. 체급(몸무게)가 높은 상대를 이긴횟수
Step3. 자신의 몸무게
Step4. 자신의 숫자

정렬조건을 한 번에 정리하기 위해서 딕셔너리와 zip을 활용했습니다. zip을 사용해 두 개의 리스트에서 한 번에 정보를 조합할 수 있습니다.

승률의 경우 위에 서술한 것 과 같이 'W'이 표기된 갯수를 count함수를 사용해서 카운팅을 해주고 'N'을 따로 카운팅해 전체 횟수(len)에서 차감해줍니다. 또한 삼항연산자를 써서 모든 선수의 모든 시합이 'N'(열린적없음)에 해당하는 예제3에 대응하도록 해줍니다.

h_point(자기보다 무거운 상대를 이긴횟수)의 경우 리스트컴프리헨션과 enumerate를 활용해서 이긴상대의 몸무게를 인덱스로 추적해서 넣어주고 len으로 횟수를 카운팅 해줍니다.

이렇게 정보가 모아져있는 info리스트를 sorted를 통해 정렬해줍니다. sorted를 다중조건으로 정렬하는 가장 손쉬운방법은 key = lambda x : (x[0], -x[1]) 처럼 lambda와 튜플을 활용하는 방식입니다. 튜플 안에 들어있는 조건의 순서대로 실행되게 됩니다. 이때 x[0] 처럼 양수면 오름차순 -x[1]처럼 음수면 내림차순 정렬로 정렬하게 되는데 문제의 정렬조건의 step1~3은 내림차순 정렬을 해줘야 되므로 음수로 써줘야합니다.

마지막으로 다 정렬된 리스트에서 반환부에서 ['no'] 번호만 반환시켜서 리스트로 반환해주면됩니다.

다른풀이

def solution(weights, head2head):
    result = []
    l = len(weights)
    # 한 번에 정렬해서 풀어봅시다!
    ans = [[0 for _ in range(4)] for _ in range(l)] # 승률, 무거운복서 이긴횟수, 자기 몸무게, 번호(음수로)
    for i in range(l):
        ans[i][2] = weights[i]
        ans[i][3] = -(i+1)
        cnt = 0 # 판수
        for j in range(l):
            if head2head[i][j] == 'W':
                ans[i][0] += 1 # 일단 이김
                cnt += 1
                if weights[i] < weights[j]:
                    ans[i][1] += 1 # 무거운 복서 이김
            elif head2head[i][j] == 'L':
                cnt += 1 # 판수만 늘려준다
        if cnt == 0:
            ans[i][0] = 0
        else:
            ans[i][0] /= cnt
    ans.sort(reverse=True) # 역순으로 정렬하면 모든 조건이 한 번에 정렬된다

    for i in range(l):
        result.append(-ans[i][3])
    return result

가장 많은 추천수를 받은 해결방식입니다.
2차원 리스트로 삽입과 동시에 정렬을 해주면서 문제를 해결해주네요.

profile
어려운 것은 없다, 다만 아직 익숙치않을뿐이다.
post-custom-banner

0개의 댓글