알고리즘

[프로그래머스 Lv3] 베스트앨범 (Python)

lwkejpf 2022. 7. 28. 22:15

 


 

from collections import defaultdict

def solution(genres, plays):
    answer = []
    genre_li = []
    
    dict_1 = defaultdict(int)
    dict_2 = defaultdict(list)
    dict_3 = defaultdict(list)

    for i in range(len(genres)) :
        dict_1[genres[i]] += plays[i]
        dict_2[(genres[i], plays[i])].append(i)
    
    dict_1 = sorted(dict_1.items(), reverse=True, key=lambda item: item[1])

    for i in dict_1 :
        genre_li.append(i[0])

    k = list(dict_2.keys())
    v = list(dict_2.values())

    for i in range(len(k)) :
        dict_3[k[i][0]].append({k[i][1] : v[i]})
        
    for gen in genre_li :
        hap = dict()
        
        for i in dict_3[gen] :
            hap.update(i)
            
        hap = sorted(hap.items(), reverse = True)
        answer.append(hap[0][1][0])
        
        if len(hap[0][1]) >= 2 :
            answer.append(hap[0][1][1])
        elif len(hap) >= 2 :
            answer.append(hap[1][1][0])
    return answer

 

 

우와....... 5시간만에 드디어 ............. 드디어 ㅠㅠㅠㅠㅠ

고작 이거 하나를 하루죙일 푼 것 같ㄴㅔ 아 ..^^

 

일단 내 기준 제일 어려웠던 건 3번 조건이었다

어차피 재생 횟수 많은 순서대로 나열하려면 내림차순하면 되는데

재생 횟수 같으면 고유 번호 낮은 순대로 수록하라해서 음...ㅇㅓ..?

 

 

암튼 풀이 설명하기 전에 선언한 변수들부터 설명해보자면

 

 

< 예시 입출력 >

 

dict_1 : 장르별 총 재생 횟수를 내림차순 정렬한 딕셔너리

ex) {'classic' : 1450, 'pop' : 3100}

 

dict_2 : 같은 장르 내 재생 횟수가 동일한 노래를 고려한 딕셔너리

Key : (장르명, 재생 횟수)

Value : 각 노래의 인덱스

 

ex) {('classic', 500) : [0], ('pop', 600) : [1], ('classic', 150) : [2] ... }

 

-> plays 의 0번 인덱스 노래는 'classic' 장르에 속하며, 500회의 재생 횟수를 가짐.

plays 의 1번 인덱스 노래는 'pop' 장르에 속하며, 600회의 재생 횟수를 가짐.

...

 

dict_3 : 노래의 재생 횟수와 인덱스를 장르별로 구분한 딕셔너리

ex) {'classic': [{500: [0]}, {150: [2]}, {800: [3]}], 'pop': [{600: [1]}, {2500: [4]}]}

 

-> 'classic' 에 속하는 노래들 중 500회의 재생 횟수를 가진 노래의 인덱스는 0번

     'classic' 에 속하는 노래들 중 150회의 재생 횟수를 가진 노래의 인덱스는 2번

     'classic' 에 속하는 노래들 중 800회의 재생 횟수를 가진 노래의 인덱스는 3번

...

 

이런 식으로 구분해줬고, defaultdict() 를 사용해서 기본 타입을 정해줬다

그럼 이제 풀이를 설명해보겠ㄷㅏ... 근데 이제 본인도 헷갈리기 시작하는 ..ㅋ

 

 

 

 

    for i in range(len(genres)) :
        dict_1[genres[i]] += plays[i]
        dict_2[(genres[i], plays[i])].append(i)
    
    dict_1 = sorted(dict_1.items(), reverse=True, key=lambda item: item[1])

 

dict_1 은 defaultdict 의 인자를 int 로 줬기 때문에 초깃값이 0 

그래서 genres 순회하면서 각 장르를 Key 로 지정해서 Value 에 갱신해줌

 

dict_2 에서 defaultdict 인자를 list 로 준 이유는 인덱스를 계속 덧붙여주려고 ..!!!

위에 써놓은 것처럼 Key 는 튜플 형태로 (장르명, 재생 횟수) 로 지정하고, 

Value 는 append() 이용해서 인덱스를 계속 덧붙여나감.

 

 

 

 

 

    k = list(dict_2.keys())
    v = list(dict_2.values())

    print("K : ", k, "\n")
    print("V : ", v, "\n")
    
    for i in range(len(k)) :
        dict_3[k[i][0]].append({k[i][1] : v[i]})
        
    print(dict_3)

 

그리고 dict_1 을 Value 기준으로 내림차순해서

재생 횟수가 높은 장르가 맨 앞에 올 수 있도록 해줌.

 

genre_li 는 총 재생 횟수 높은 순서대로 저장해준 장르 리스트인데,

이걸 따로 구해준 이유는 총 재생 횟수 높은 순서대로 수록해야 되기 때문 ..!!!

 

 

 

 

 

	
    k = list(dict_2.keys())
    v = list(dict_2.values())

    print("K : ", k, "\n")
    print("V : ", v, "\n")
    
    for i in range(len(k)) :
        dict_3[k[i][0]].append({k[i][1] : v[i]})
        
    print(dict_3)

 

 

여기서부터는 나도 엄청 헷갈려하고 혼란스러워함 .. ^-^

차라리 출력문 보면서 이해하는 게 나을 듯

 

k : dict_2 의 Key 를 모아둔 리스트 => 튜플 형태 (장르명, 재생 횟수)

v : dict_2 의 Value 를 모아둔 리스트 => 노래의 인덱스

 

그리고 k 와 v 를 이용해서 dict_3 을 생성해줬다

dict_3 도 마찬가지로 defaultdict 인자로 list 를 지정해줬음.

 

dict_2 와 달리 튜플 형태가 아닌 장르 하나로 Key 를 지정해주고,

Value 에는 {재생횟수 : 인덱스} 딕셔너리가 존재하는 형태

 

그냥 딕셔너리 안에 또 다른 여러 개의 딕셔너리가 있다고 보면 됨 ..!!

 

 

 

 

    for gen in genre_li :
        hap = dict()
        
        for i in dict_3[gen] :
            hap.update(i)
            
        print("장르 ", gen, "정렬 전 HAP : ", hap)
        
        hap = sorted(hap.items(), reverse = True)
        print("장르 ", gen, "정렬 후 HAP : ", hap, "\n")
        
        print("HAP[0][1][0] : ", hap[0][1][0])
        answer.append(hap[0][1][0])
        
        if len(hap[0][1]) >= 2 :
            # 같은 장르 안에서 재생 횟수가 동일한 노래가 2개 이상일 때
            print("조건 1) HAP[0][1][1] : ", hap[0][1][1])
            answer.append(hap[0][1][1])
        elif len(hap) >= 2 :
            # 같은 장르 안에서 재생 횟수가 여러 개일 때
            print("조건 2) HAP[1][1][0] : ", hap[1][1][0])
            answer.append(hap[1][1][0])

 

 

hap 은 뭐냐면 .. 

현재 dict_3 은 장르별로 Value 에 여러 개의 딕셔너리가 존재하니까

하나로 합쳐주려고 update() 메서드 이용해서 합친 딕셔너리임.

 

update 는 딕셔너리끼리 병합해주는 메서드로, 

예를 들어서 딕셔너리 a, b 가 있다고 가정할 때

a.update(b) 하면 둘이 합쳐져서 하나의 딕셔너리를 만들어냄.

 

암튼 hap 을 Key 기준으로 내림차순해서

장르 안에서 제일 높은 재생 횟수와 해당 노래의 인덱스를 알아냄.

 

그리고 어차피 장르별로 최대 2개까지만 노래를 수록할 수 있기 때문에

제일 처음 노래의 인덱스는 무조건 저장하고,

 

나머지 하나 노래는 2가지 경우로 나눠서 if-elif 문으로 작성해준거임.

 

if 조건문은 같은 장르 안에서 재생 횟수가 동일한 노래가 2개 이상일 때,

elif 조건문은 같은 장르 안에서 재생 횟수가 여러 개일 때

 

 

 

지금 입출력 예시는 같은 재생 횟수를 가진 노래가 없기 때문에

이해가 잘 안 갈수도 있으니까 아래 참고하시길 ,, 그럼 전 이만