[Python] BOJ 백준 21939 문제 추천 시스템 Version 1

김민규·2022년 9월 27일
0

Python

목록 보기
10/13

문제


tony9402는 최근 깃헙에 코딩테스트 대비 문제를 직접 뽑아서 "문제 번호난이도"로 정리해놨다.

깃헙을 이용하여 공부하시는 분들을 위해 새로운 기능을 추가해보려고 한다.

만들려고 하는 명령어는 총 3가지가 있다. 아래 표는 각 명령어에 대한 설명이다.

ColumnDescription
recommend x x가 1인 경우 추천 문제 리스트에서 가장 어려운 문제의 번호를 출력한다. 만약 가장 어려운 문제가 여러 개라면 문제 번호가 큰 것으로 출력한다.  x가 -1인 경우 추천 문제 리스트에서 가장 쉬운 문제의 번호를 출력한다. 만약 가장 쉬운 문제가 여러 개라면 문제 번호가 작은 것으로 출력한다.
add P L추천 문제 리스트에 난이도가 L인 문제 번호 P를 추가한다. (추천 문제 리스트에 없는 문제 번호 P만 입력으로 주어진다. 이전에 추천 문제 리스트에 있던 문제 번호가 다른 난이도로 다시 들어 올 수 있다.)
solved P                      추천 문제 리스트에서 문제 번호 P를 제거한다. (추천 문제 리스트에 있는 문제 번호 P만 입력으로 주어진다.)

명령어 recommend는 추천 문제 리스트에 문제가 하나 이상 있을 때만 주어진다.

명령어 solved는 추천 문제 리스트에 문제 번호가 하나 이상 있을 때만 주어진다.

위 명령어들을 수행하는 추천 시스템을 만들어보자.


풀이


노가다로 해결한 문제. 단순히 분기를 나누고 min, max값을 구해서 해결할 수 있다고 생각했는데, 어림도 없었다.

이런 저런 방법들을 전부 사용해보다가, 같은 스터디원이 heap을 쓰다가 포기했다는 것을 듣고 heap으로 구현해봤다.

아래는 초기 실패 코드

import sys
from collections import Counter

si = sys.stdin.readline
def MIIS(): return map(int, si().split())


n = int(si())

prob = {k: [] for k in range(101)}
idx = {}
for _ in range(n):
    num, difficulty = MIIS()
    prob[difficulty].append(num)
    idx[num] = difficulty

_max = max(x for x in prob if prob[x])
_min = min(x for x in prob if prob[x])
m = int(si())
ret = []

for _ in range(m):
    line = si().split()
    if line[0] == 'add':
        num, diff = map(int, line[1:])
        if diff < _min:
            _min = diff
        elif diff > _max:
            _max = diff
        prob[diff].append(num)
        idx[num] = diff
    elif line[0] == 'solved':
        num = int(line[1])
        if num in prob[_min]:
            prob[idx[num]].remove(num)
            _min = min(x for x in prob if prob[x])
        elif num in prob[_max]:
            prob[idx[num]].remove(num)
            _max = max(x for x in prob if prob[x])
        else:
            prob[idx[num]].remove(num)
    elif line[0] == 'recommend':
        if line[1] == '-1':
            ret.append(sorted(prob[_min])[0])
        else:
            ret.append(sorted(prob[_max])[-1])

for a in ret:
    print(a)

삽질의 흔적...

아래는 성공한 코드이다. 

defaultdict를 이용하면서 해결 했는 지 체크하고, 명령어에 따라 min_heap과 max_heap을 적절히 섞어가며 사용했다.

애를 먹었던 부분은 add하는 부분에서 같은 문제가 해결되고 다른 난이도로 다시 들어올 수 있다는 사실을 간과했던 것이다.

이를 막기 위해 add에서도 해결된 문제들을 전부 빼주는 방식으로 진행했더니 해결되었다. 

from collections import defaultdict
import sys
import heapq

si = sys.stdin.readline
def MIIS(): return map(int, si().split())


n = int(si())

# recommend 용
min_heap = []
max_heap = []
ret = []

visited = defaultdict()

for _ in range(n):
    num, difficulty = MIIS()
    heapq.heappush(min_heap, (difficulty, num))
    heapq.heappush(max_heap, (-difficulty, -num))
    visited[num] = False
m = int(si())
for _ in range(m):
    line = si().split()
    if line[0] == 'add':
        num, difficulty = map(int, line[1:])
        while visited[(min_heap[0][1])]:
            heapq.heappop(min_heap)
        while visited[-(max_heap[0][1])]:
            heapq.heappop(max_heap)
        heapq.heappush(min_heap, (difficulty, num))
        heapq.heappush(max_heap, (-difficulty, -num))
        visited[num] = False

    elif line[0] == 'solved':
        visited[int(line[1])] = True

    else:
        if line[1] == '1':
            while visited[-(max_heap[0][1])]:
                heapq.heappop(max_heap)
            ret.append(-max_heap[0][1])
        else:
            while visited[min_heap[0][1]]:
                heapq.heappop(min_heap)
            ret.append(min_heap[0][1])


for ans in ret:
    print(ans)
profile
Smart Contract Developer

0개의 댓글