[BOJ 1202] 보석 도둑 (Python)

박지훈·2021년 4월 8일
0

문제

링크



풀이

Knapsack 문제로 접근하였으나 아니였다. 가방이 여러개가 있으며 가방 하나에는 딱 하나의 보석만 담을 수 있다는 조건이 있다. 따라서 최대한 비싼 보석을 하나라도 더 훔치는 것이 중요하다.

비싼 보석인데 무겁다고 해서 더 싸고 가벼운 보석에게 가방을 양보하게 되면 훔칠 수 있는 보석의 가격이 작아지므로 이는 의미없는 행동이 된다. 포인트는 최대한 비싼 보석부터 최대한 작은 가방에 담는 것이다!
전체적인 로직은 아래와 같다.

  1. 가방에 담을 수 있는 최대 무게를 기준으로 오름차순 정렬한다.

  2. 보석의 무게 <= 가방의 무게이면 가방에 담아서 훔친다. 단, 보석의 무게 <= 가방의 무게인 보석들 중에 가격이 가장 비싼 보석 하나를 훔쳐야한다.

  3. 모든 가방과 모든 보석에 대해 위 과정을 반복한다.

처음 구현할 때 보석의 무게를 기준으로 내림차순 정렬, 가방에 담을 수 있는 최대 무게를 기준으로 오름차순 정렬한 후 보석을 가방에 담을 수 있다면 바로 훔쳤고, 방문 배열을 통해 방문처리를 해주었다. 하지만, 시간초과가 발생하였다. 이렇게 풀면 O(NK) 시간복잡도가 나온다. 300,000 x 300,000 = 90,000,000,000로 당연히 시간초과가 나게된다.

해답 포인트는 우선순위 큐 [O(NlogN)] (시간복잡도를 낮춰야하기 때문에 사용함!)를 사용하는 것이다. 우선순위 큐를 사용해 가방에 담을 수 있는 보석을 탐색할 때 최소힙으로 구현한 우선순위 큐를 사용한다. 가방에 담을 수 있는 보석들 중 가장 비싼 보석을 훔칠 때 최대힙으로 구현한 우선순위 큐를 사용한다. 여기서 최소힙은 오름차순 이진트리, 최대힙은 내림차순 이진트리라는 것만 알고 넘어가자. 추후에 자세히 설명할 예정이다.

Python에서 힙은 최소힙으로 구성되어 있다. 따라서 최대힙으로 구현한 우선순위 큐를 사용하려면 원소의 부호를 임시로 변경하는 방식을 사용하면 된다. (-부호 붙이는 방식)



코드

import sys
import heapq

input = sys.stdin.readline
N, K = map(int, input().split())
jewels = []
for _ in range(N):
    heapq.heappush(jewels, list(map(int, input().split())))

backpacks = []
for _ in range(K):
    backpacks.append(int(input()))
backpacks = sorted(backpacks)

ans = 0
possible = []
for backpack in backpacks:
    while jewels and backpack >= jewels[0][0]:
        heapq.heappush(possible, -heapq.heappop(jewels)[1])
    if possible:
        ans -= heapq.heappop(possible)

print(ans)

시간초과 코드 (정답 X)

# Python3 / PyPy3 모두 시간 초과
import sys

input = sys.stdin.readline
N, K = map(int, input().split())
jewels = []
for _ in range(N):
    x, y = map(int, input().split())
    jewels.append((x, y))
jewels = sorted(jewels, key=lambda x: -x[1])
visited_jewels = [False] * N

backpacks = []
for _ in range(K):
    backpacks.append(int(input()))
backpacks = sorted(backpacks)
visited_backpacks = [False] * K

ans = 0
for i in range(len(backpacks)):
    for k in range(len(jewels)):
        if jewels[k][0] <= backpacks[i] and not visited_backpacks[i] and not visited_jewels[k]:
            ans += jewels[k][1]
            visited_jewels[k] = True
            visited_backpacks[i] = True

print(ans)
profile
Computer Science!!

0개의 댓글