그리디
Summary
그리디는 “지금 최선의 선택이 전체 최선이 되는가”를 확인해야 하는 유형이다.
코테에서는 보통 정렬과 함께 나온다. 정렬 기준을 잘못 잡으면 바로 틀린다.
그리디 의심 신호
| 표현 | 처리 |
|---|---|
| 정렬 후 앞에서부터 선택 | 회의실 배정, 활동 선택 |
| 끝나는 시간 기준 정렬 | 최대 개수 선택 |
| 작은 것부터 / 큰 것부터 | 로프, 동전, 거스름돈 |
| 교환해도 손해 안 봄(교환 논증) | 그리디 성립 근거 |
Warning
“그리디 같다”고 바로 짜지 말고, 반례가 없는지 먼저 의심한다.
반례가 떠오르면 DP를 고려한다.
회의실 배정 검증 템플릿
Info
끝나는 시간 오름차순 정렬 후, 시작 시간이 직전 끝 시간 이상이면 선택한다.
끝나는 시간이 같으면 시작 시간 오름차순으로 정렬해야 안전하다.
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine().trim());
int[][] meet = new int[n][2];
for (int i = 0; i < n; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
meet[i][0] = Integer.parseInt(st.nextToken());
meet[i][1] = Integer.parseInt(st.nextToken());
}
Arrays.sort(meet, (a, b) -> {
if (a[1] == b[1]) return Integer.compare(a[0], b[0]); // 끝 같으면 시작 오름차순
return Integer.compare(a[1], b[1]); // 끝나는 시간 오름차순
});
int end = 0, count = 0;
for (int[] m : meet) {
if (m[0] >= end) { // 직전 회의 끝난 뒤 시작 가능
count++;
end = m[1];
}
}
System.out.println(count);
}
}로프 (최댓값 그리디)
Info
N개 중 일부를 묶으면 묶음 중 최소 굵기 * 개수가 들 수 있는 무게다.
내림차순 정렬 후arr[i] * (몇 번째까지 묶었는지)의 최댓값을 본다.
Arrays.sort(arr); // 오름차순
int answer = 0;
for (int i = 0; i < n; i++) {
// arr[i]를 최소 굵기로 쓰면, i부터 끝까지 (n - i)개를 묶을 수 있다
answer = Math.max(answer, arr[i] * (n - i));
}주의점
| 실수 | 주의 |
|---|---|
| 정렬 기준 오선택 | 무엇을 기준으로 정렬할지 먼저 결정 |
| 끝 시간 동률 처리 | 시작 시간 보조 정렬로 안전하게 |
| 반례 미확인 | 교환 논증이 안 되면 DP 의심 |
a - b 비교 | overflow. Integer.compare() 사용 |
| 비용/개수 합 | 클 수 있으면 long |
백지 복원
1. 회의실 배정은 무엇을 기준으로 정렬하는가?
2. 끝나는 시간이 같을 때 어떻게 정렬하는가?
3. 그리디가 의심될 때 DP로 넘어가야 하는 신호는?