문제
정렬된 두 묶음의 숫자 카드가 있다고 하자. 각 묶음의 카드의 수를 A, B라 하면 보통 두 묶음을 합쳐서 하나로 만드는 데에는 A+B 번의 비교를 해야 한다. 이를테면, 20장의 숫자 카드 묶음과 30장의 숫자 카드 묶음을 합치려면 50번의 비교가 필요하다.
매우 많은 숫자 카드 묶음이 책상 위에 놓여 있다. 이들을 두 묶음씩 골라 서로 합쳐나간다면, 고르는 순서에 따라서 비교 횟수가 매우 달라진다. 예를 들어 10장, 20장, 40장의 묶음이 있다면 10장과 20장을 합친 뒤, 합친 30장 묶음과 40장을 합친다면 (10 + 20) + (30 + 40) = 100번의 비교가 필요하다. 그러나 10장과 40장을 합친 뒤, 합친 50장 묶음과 20장을 합친다면 (10 + 40) + (50 + 20) = 120 번의 비교가 필요하므로 덜 효율적인 방법이다.
N개의 숫자 카드 묶음의 각각의 크기가 주어질 때, 최소한 몇 번의 비교가 필요한지를 구하는 프로그램을 작성하시오.
입력
첫째 줄에 N이 주어진다. (1 ≤ N ≤ 100,000) 이어서 N개의 줄에 걸쳐 숫자 카드 묶음의 각각의 크기가 주어진다. 숫자 카드 묶음의 크기는 1,000보다 작거나 같은 양의 정수이다.
출력
첫째 줄에 최소 비교 횟수를 출력한다.
예제 입력 1
3
10
20
40
예제 출력 1
100
문제 풀이
1. 가장 작은 묶음부터 합쳐야 한다: 카드 묶음을 합칠 때마다 비교 횟수가 발생하고, 합쳐진 묶음은 다음 단계의 연산에 계속 포함됩니다. 즉, 초반에 합친 카드 묶음의 크기가 작을수록, 전체 비교 횟수가 줄어듭니다.
- 예를 들어 10, 20, 40이 있을 때:
- (10+20)을 먼저 하면: 30이 됨. (비용 30 발생) → 남은 것: 30, 40
- (30+40)을 하면: 70이 됨. (비용 70 발생)
- 총 비용: 30 + 70 = 100
- 만약 큰 것(20+40)을 먼저 하면:
- (20+40)을 먼저 하면: 60이 됨. (비용 60 발생) → 남은 것: 10, 60
- (10+60)을 하면: 70이 됨. (비용 70 발생)
- 총 비용: 60 + 70 = 130 (더 비효율적)
따라서 현재 나와 있는 카드 묶음 중 가장 작은 2개를 계속해서 선택해야 합니다.
2. 자료구조 선택: 우선순위 큐
매번 가장 작은 값 2개를 찾고, 합친 결과를 다시 넣어서 정렬해야 합니다. 일반 리스트를 매번 정렬하면 시간 복잡도가 너무 커져서 시간 초과가 납니다. 따라서 삽입/삭제 시 자동으로 정렬 상태를 유지해 주는 heapq (최소 힙)를 사용해야 합니다.
답안 코드
import sys
import heapq
# 1. 입력 받기
input = sys.stdin.readline
N = int(input())
cards = []
for _ in range(N):
heapq.heappush(cards, int(input()))
# 2. 그리디 알고리즘
total_cost = 0
# 카드 묶음이 1개만 남을 때까지 반복
while len(cards) > 1:
# 가장 작은 묶음 2개 꺼내기 pop
first = heapq.heappop(cards)
second = heapq.heappop(cards)
# 두 묶음을 합침
sum_val = first + second
# 비교 횟수 누적
total_cost += sum_val
# 합친 묶음을 다시 힙에 넣기 push
heapq.heappush(cards, sum_val)
print(total_cost)
코드 설명
- heapq.heappush: 입력받은 카드 묶음들을 힙에 넣습니다. 파이썬의 heapq는 기본적으로 최소 힙(Min-Heap)이므로, 가장 작은 숫자가 항상 0번째 인덱스(루트)에 오게 됩니다.
- while len(cards) > 1: 묶음이 하나로 합쳐질 때까지 반복합니다.
- heappop 2번: 가장 작은 카드 묶음(first)과 두 번째로 작은 카드 묶음(second)을 꺼냅니다.
- total_cost += sum_val: 두 묶음을 합치는 데 발생한 비용을 전체 비용에 더합니다.
- heappush(cards, sum_val): 합쳐진 새 묶음도 나중에 다른 묶음과 합쳐져야 하므로, 다시 힙에 넣습니다. 이때 힙은 자동으로 크기 순서를 재정렬합니다.
- 예외 처리 일 경우, 비교할 대상이 없으므로 비교 횟수는 0입니다. 위 코드는 while len(cards) > 1 조건 덕분에 일 때 루프를 돌지 않고 0을 올바르게 출력하므로 별도의 예외 처리를 하지 않아도 됩니다.
'백준' 카테고리의 다른 글
| [프로그래머스] 양궁대회 (1) | 2026.02.11 |
|---|---|
| [백준] 14719번 - 빗물 (0) | 2026.02.10 |
| [백준] 2493번 - 탑 (0) | 2026.02.09 |
| [프로그래머스] 이모티콘할인행사 (0) | 2026.02.09 |
| [백준] 1863번 - 스카이라인 쉬운거 (0) | 2026.02.07 |