개념
그리디는 지금 당장 가장 좋아 보이는 걸 집는 방식이에요. 뷔페에서 눈앞의 제일 맛있어 보이는 접시를 바로 집는 것과 같아요. 문제는, 지금 집은 게 나중에 후회를 만들지 않는다는 걸 보장해야 한다는 거예요. 후회가 없다고 확신이 서면 그냥 집으면 되고(짧고 빠른 코드), 확신이 안 서면 모든 경우를 따지는 DP로 가야 해요. 그래서 그리디의 진짜 공부는 코드가 아니라 '이 선택이 후회 없는가'를 판단하는 눈이에요.그리디는 매 순간 가장 좋아 보이는 것을 고르고 뒤돌아보지 않는 방식이에요. 코드가 짧고 빨라요. 문제는 언제 이게 옳은가이고, 그걸 판단하는 게 이번 주의 전부예요.말은 어렵지만 실전에서 확인하는 방법은 단순해요. 반례를 먼저 찾아보는 거예요. 5분 안에 반례가 안 나오면 그리디로 밀어붙이고, 반례가 나오면 DP로 가요. 증명하려 들면 시험 시간이 사라져요.
회의실 배정에서 '시작이 빠른 것'이나 '짧은 것'을 고르면 왜 안 되는지 직접 반례를 그려 봐요. 긴 회의 하나가 짧은 회의 둘을 막는 그림이 바로 나와요. 이렇게 기준을 바꿔가며 반례를 찾는 연습이 그리디의 훈련법이에요.
그리디가 통하려면
- 탐욕 선택 속성 — 지금의 최선을 골라도 최적해를 놓치지 않는다
- 최적 부분 구조 — 그 선택을 하고 남은 문제의 최적해를 더하면 전체 최적해가 된다
자주 나오는 형태
| 문제 | 무엇을 기준으로 정렬 | 왜 |
|---|---|---|
| 회의실 배정 | 끝나는 시각 오름차순 | 빨리 끝날수록 뒤에 남는 시간이 많다 |
| 구명보트 | 무게 오름차순 + 양끝 포인터 | 가장 무거운 사람은 가장 가벼운 사람과 짝지어야 이득 |
| 동전 거스름돈 (배수 화폐) | 큰 단위부터 | 큰 단위가 작은 단위의 배수라 손해가 없다 |
| 최소 회의실 개수 | 시작 시각 + 힙 | 가장 빨리 끝나는 방을 재사용 |
직접 따라가 보기: 회의실 배정
회의 네 개가 있어요. 겹치지 않게 최대한 많이 잡는 게 목표예요. 끝나는 시각이 빠른 순으로 먼저 정렬해요. 빨리 끝날수록 뒤에 남는 시간이 많으니까요. 그다음 앞에서부터 하나씩 보면서, 마지막에 고른 회의가 끝난 시각(lastEnd) 이후에 시작하는 회의만 골라요.끝나는 시각 오름차순으로 정렬한 뒤, 안 겹치는 것만 골라요정렬 후 (끝나는 시각 기준)A 1 ~ 3B 2 ~ 5C 4 ~ 7D 6 ~ 8lastEnd = -inf (아직 아무것도 안 골랐어요)A 시작 1 >= -inf ? yes -> 고름 lastEnd = 3B 시작 2 >= 3 ? no -> 건너뜀C 시작 4 >= 3 ? yes -> 고름 lastEnd = 7D 시작 6 >= 7 ? no -> 건너뜀고른 회의 = A, C -> 최대 2개
B를 건너뛴 게 손해처럼 보여도, B를 골랐다면 lastEnd가 5가 돼서 C도 D도 못 잡아 오히려 하나만 남아요. 가장 빨리 끝나는 걸 고르는 선택이 뒤에 가장 넓은 자리를 남긴다는 게 이 그리디가 후회 없는 이유예요.그리디가 깨지는 순간
이번 주의 마지막 문제가 이 지점이에요. 동전이 1, 5, 10처럼 배수 관계면 큰 것부터 집는 그리디가 맞아요. 그런데 동전이 1, 3, 4이고 6원을 만든다면?- 그리디: 4 + 1 + 1 = 3개
- 최적: 3 + 3 = 2개
즉 그리디로 안 되는 문제란 '지금의 선택이 나중에 후회를 만드는' 문제예요. 후회를 없애려면 모든 선택지를 다 따져봐야 하는데, 그것을 중복 없이 하는 방법이 동적 계획법이에요.
패턴 코드
정렬 후 하나씩 집기 (회의실 배정)끝나는 시각 기준 정렬이 핵심. 시작 시각 기준으로 하면 틀려요.
javascriptCopy codemeetings.sort((a, b) => a[1] - b[1]);let count = 0;let lastEnd = -Infinity;for (const [start, end] of meetings) {if (start >= lastEnd) {count++;lastEnd = end;}}
정렬 후 양끝에서 짝짓기 (구명보트)
javascriptCopy codepeople.sort((a, b) => a - b);let i = 0;let j = people.length - 1;let boats = 0;while (i <= j) {if (people[i] + people[j] <= limit) i++;j--;boats++;}
이번 주 문제
이번 주 진행0 / 4
셀프 체크
다음 주로 넘어가기 전에 소리 내어 답해 보세요. 막히면 그 부분이 아직 덜 익은 개념이에요.- 회의실 배정에서 '시작이 빠른 순'으로 고르면 안 되는 반례를 그릴 수 있는가?
- 동전 1, 3, 4로 6원을 만들 때 그리디가 왜 지는가?
- 그리디를 쓸지 DP를 쓸지 판단할 때 스스로에게 던지는 질문은 무엇인가?
- 회의실 배정에서 가장 빨리 끝나는 회의를 고르면 왜 뒤에 자리가 가장 많이 남나요?