Lesson 매 순간 최선이 전체 최선이 되는 조건
그리디란
그리디(greedy, 탐욕법) 는 매 단계에서 지금 당장 가장 좋아 보이는 선택을
하고 절대 되돌리지 않는 전략입니다. 이 지역적 최선의 연쇄가 전역 최적해와
일치할 때만 옳습니다.
언제 옳은가 — 두 가지 성질
그리디가 정답을 보장하려면 대개 다음을 만족합니다.
- 탐욕적 선택 속성(greedy choice property): 지역적으로 최선인 선택이
전역 최적해의 일부가 된다. - 최적 부분 구조(optimal substructure): 한 번 선택하고 남은 문제의
최적해가 전체 최적해로 이어진다.
이 성질이 없으면 그리디는 틀립니다. 예를 들어 동전 \(\{1, 3, 4\}\)로
\(6\)을 만들 때 큰 동전부터 고르는 그리디는 \(4 + 1 + 1 = 3\)개를 주지만, 최적은
\(3 + 3 = 2\)개입니다. 즉 동전 그리디는 특정 화폐 체계에서만 옳습니다.
정당성 증명: 교환 논증(exchange argument)
그리디가 옳음을 보이는 표준 기법입니다. "어떤 최적해가 그리디의 선택과
다르다면, 그리디의 선택으로 바꿔치기(swap) 해도 최소한 같은 품질이
유지된다"를 보이면, 그리디 해도 최적임이 따라옵니다.
대표 예: 회의실 배정(활동 선택)
시작·종료 시간이 있는 회의들 중 겹치지 않게 최대 개수를 고르는 문제.
종료 시간이 빠른 순으로 정렬해 앞에서부터 고르는 그리디가 최적입니다.
직관: 가장 일찍 끝나는 회의를 고르면 뒤에 남는 시간이 최대가 되어, 이후
더 많은 회의를 담을 수 있습니다. 교환 논증으로 엄밀히 증명됩니다.
// 회의 (start, end). 종료 시간 오름차순 정렬 후 그리디
sort(v.begin(), v.end(), [](auto& a, auto& b){ return a.second < b.second; });
int cnt = 0, last_end = -1;
for (auto& [s, e] : v)
if (s >= last_end) { cnt++; last_end = e; } // 겹치지 않으면 선택
v.sort(key=lambda x: x[1]) # 종료 시간 기준
cnt, last_end = 0, float("-inf")
for s, e in v:
if s >= last_end:
cnt += 1
last_end = e
언제 그리디를 떠올리나
- "최대/최소 개수", "최소 비용"인데 정렬 후 순서대로 고르면 될 것 같을 때.
- 각 단계의 최선이 나중을 망치지 않을 것 같은 직관이 설 때.
- 단, 직관은 반드시 증명 또는 반례 검증으로 확인해야 합니다.