[백준] 1202 보석 도둑
2025년 08월 19일
38
[Gold II] 보석 도둑 - 1202
성능 요약
메모리: 107892 KB, 시간: 1396 ms
분류
자료 구조, 그리디 알고리즘, 정렬, 우선순위 큐
문제 설명
세계적인 도둑 상덕이는 보석점을 털기로 결심했다.
상덕이가 털 보석점에는 보석이 총 N개 있다. 각 보석은 무게 Mi와 가격 Vi를 가지고 있다. 상덕이는 가방을 K개 가지고 있고, 각 가방에 담을 수 있는 최대 무게는 Ci이다. 가방에는 최대 한 개의 보석만 넣을 수 있다.
상덕이가 훔칠 수 있는 보석의 최대 가격을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 N과 K가 주어진다. (1 ≤ N, K ≤ 300,000)
다음 N개 줄에는 각 보석의 정보 Mi와 Vi가 주어진다. (0 ≤ Mi, Vi ≤ 1,000,000)
다음 K개 줄에는 가방에 담을 수 있는 최대 무게 Ci가 주어진다. (1 ≤ Ci ≤ 100,000,000)
모든 숫자는 양의 정수이다.
출력
첫째 줄에 상덕이가 훔칠 수 있는 보석 가격의 합의 최댓값을 출력한다.
문제 풀이
문제 분석
이 문제는 보석의 무게와 가격, 그리고 가방의 최대 무게 제한이 주어졌을 때, 가방에 담을 수 있는 보석 가격의 합의 최댓값을 구하는 문제입니다. 각 가방에는 하나의 보석만 담을 수 있습니다.
접근 방법
이 문제는 그리디 알고리즘과 우선순위 큐(heapq)를 사용하여 해결할 수 있습니다. 핵심 아이디어는 다음과 같습니다.
- 가방을 오름차순으로 정렬: 무게 제한이 작은 가방부터 고려합니다.
- 보석을 무게가 작은 순서대로 정렬: 가방에 넣을 수 있는 보석을 쉽게 찾기 위함입니다. (코드에서는 보석을 가격이 높은 순서로 정렬해놓고, while문에서 pop()을 사용해 무게가 작은 순서대로 접근하고 있습니다.)
- 가방마다 넣을 수 있는 보석 중 가장 비싼 보석을 선택: 힙(우선순위 큐)을 사용하여 가방에 넣을 수 있는 보석들의 가격을 관리하고, 가장 높은 가격의 보석을 선택합니다.
구현 설명
1. 입력 및 초기화
n, k = map(int, input().split()): 보석의 개수 N과 가방의 개수 K를 입력받습니다.jewels = []: 보석 정보를 저장할 리스트를 초기화합니다.bags = []: 가방 정보를 저장할 리스트를 초기화합니다.for i in range(n): jewels.append(list(map(int, input().split()))): 보석의 무게와 가격을 입력받아jewels리스트에 저장합니다.for i in range(k): bags.append(int(input())): 가방의 최대 무게를 입력받아bags리스트에 저장합니다.
2. 정렬
jewels.sort(reverse=True): 보석을 가격이 높은 순서대로 정렬합니다. (무게가 작은 순서로 정렬하고 가방에 담을 수 있는지 확인하는 방식도 가능)bags.sort(): 가방을 무게 제한이 작은 순서대로 정렬합니다.
3. 가방에 보석 담기
can_take = []: 현재 가방에 넣을 수 있는 보석들의 가격을 저장하는 최소 힙(min heap)입니다. (파이썬의heapq는 최소 힙을 제공하므로, 가격에 음수를 취해 최대 힙처럼 사용합니다.)answer = 0: 훔칠 수 있는 보석 가격의 합을 저장할 변수를 초기화합니다.for bag in bags:: 각 가방에 대해 반복합니다.while jewels:: 아직 확인하지 않은 보석이 있는 동안 반복합니다.if jewels[-1][0] <= bag:: 현재 보석의 무게가 가방의 최대 무게보다 작거나 같으면 (가방에 넣을 수 있으면)weight, value = jewels.pop(): 보석 리스트에서 해당 보석을 꺼내고 무게와 가격을 저장합니다.heapq.heappush(can_take, -value): 보석의 가격에 음수를 취하여can_take힙에 넣습니다.else: break: 현재 보석의 무게가 가방의 최대 무게보다 크면, 더 이상 현재 가방에 넣을 수 있는 보석이 없으므로 내부 while 루프를 종료합니다.if can_take:: 현재 가방에 넣을 수 있는 보석이 하나 이상 있다면,answer -= heapq.heappop(can_take):can_take힙에서 가장 작은 값(절댓값이 가장 큰 음수, 즉 가장 비싼 보석)을 꺼내어answer에 더합니다.
4. 결과 출력
print(answer): 훔칠 수 있는 보석 가격의 합의 최댓값을 출력합니다.
⏱복잡도 분석
-
시간 복잡도:
- 보석 정렬: O(N log N)
- 가방 정렬: O(K log K)
- 가방에 보석을 담는 과정: O(N log N + K log N) (각 가방에 대해 최대 N개의 보석을 검사하고, 힙 연산은 log N)
- 전체 시간 복잡도: O(N log N + K log K + N log N + K log N) = O((N+K)log(N+K)) (N과 K중 큰 값에 의해 좌우됨)
-
공간 복잡도:
jewels: O(N)bags: O(K)can_take: O(N) (최악의 경우 모든 보석이 힙에 들어갈 수 있음)- 전체 공간 복잡도: O(N + K)
핵심 포인트
- 정렬 활용: 보석과 가방을 정렬하여 효율적인 탐색이 가능하게 합니다. 가방을 무게 제한이 작은 순서대로 처리하는 것이 중요합니다.
- 우선순위 큐(힙) 활용: 각 가방에 넣을 수 있는 보석 중 가장 비싼 보석을 빠르게 찾기 위해 힙을 사용합니다.
- 그리디 접근 방식: 각 가방마다 최적의 선택(가장 비싼 보석)을 함으로써 전체적인 최적해를 찾습니다.
풀이 코드
import sys
import heapq
input = sys.stdin.readline
n,k = map(int, input().split())
jewels = []
for i in range(n):
jewels.append(list(map(int, input().split())))
bags= []
for i in range(k):
bags.append(int(input()))
jewels.sort(reverse=True)
bags.sort()
can_take = []
answer = 0
for bag in bags:
while jewels:
if jewels[-1][0] <= bag:
weight, value = jewels.pop()
heapq.heappush(can_take, -value)
else:
break
if can_take:
answer -= heapq.heappop(can_take)
print(answer)
댓글을 불러오는 중...