그리디와 동적 계획법
그리디 선택과 동적 계획법의 상태·점화식을 비교하고 적용 조건을 판단한다.
1. 그리디 알고리즘
그리디 알고리즘은 각 단계에서 현재 가장 좋아 보이는 선택을 하고 이전 선택을 되돌리지 않는다. 구현은 단순하고 빠른 경우가 많지만, 모든 문제에서 최적해를 보장하지 않는다.
최적해를 보장하려면 문제에 다음 성질이 필요하다.
- 그리디 선택 속성: 현재의 최적 선택을 포함하는 전체 최적해가 존재한다.
- 최적 부분구조: 전체 최적해가 부분 문제의 최적해로 구성된다.
대표 예는 활동 선택, 최소신장트리의 Kruskal·Prim, 음수가 없는 최단경로의 Dijkstra, 특정 화폐 체계의 동전 선택이다.
동전이 1, 5, 10, 50이라면 큰 동전부터 선택해도 최소 개수가 되지만, 동전이 1, 3, 4이고 금액이 6이면 그리디는 4+1+1의 3개를 고르는 반면 최적은 3+3의 2개이다.
2. 동적 계획법(DP)
동적 계획법은 같은 부분 문제가 반복되고 최적 부분구조가 있을 때 결과를 저장하여 중복 계산을 제거한다.
구성 절차
- 상태 정의:
dp[i]또는dp[i][j]가 무엇을 뜻하는지 정한다. - 점화식: 작은 상태에서 큰 상태를 계산하는 관계를 세운다.
- 초기값: 가장 작은 문제의 답을 지정한다.
- 계산 순서: 필요한 선행 상태가 먼저 계산되도록 한다.
- 정답 위치: 최종 답이 어느 상태에 있는지 확인한다.
메모이제이션과 테이블 방식
- 메모이제이션: 재귀적으로 필요한 상태만 계산해 저장한다.
- 테이블 방식: 작은 상태부터 반복문으로 채운다.
피보나치 수를 단순 재귀로 계산하면 같은 값을 반복 계산하지만, DP를 사용하면 각 값을 한 번만 계산해 O(n)에 해결할 수 있다.
3. 대표 DP 문제
- 최소 동전 수
- 0/1 배낭 문제
- 최장 공통 부분수열(LCS)
- 격자 경로 수
- 편집 거리
상태 의미와 점화식이 올바른지, 반복 순서가 맞는지를 함께 검증한다.
4. 비교
| 항목 | 그리디 | 동적 계획법 |
|---|---|---|
| 선택 | 현재 최선 선택을 확정 | 여러 선택 결과를 비교 |
| 이전 선택 수정 | 보통 하지 않음 | 저장된 상태를 통해 비교 |
| 필요 성질 | 그리디 선택 속성 | 중복 부분문제·최적 부분구조 |
| 장점 | 빠르고 단순 | 더 넓은 최적화 문제 해결 |
| 주의 | 반례 확인 필요 | 상태 수와 메모리 증가 |
5. 그리디 정당성 증명
그리디는 “지금 가장 좋아 보인다”만으로 충분하지 않다. 대표 증명 방식은 다음과 같다.
- 교환 논증: 어떤 최적해의 첫 선택을 그리디 선택으로 바꾸어도 최적성을 잃지 않음을 보임
- 앞서가기 논증: 매 단계까지 그리디 해가 다른 해보다 뒤처지지 않음을 보임
- 컷 속성: MST에서 어떤 컷을 가로지르는 최소 가중치 간선이 안전함을 사용
반례 하나만 있어도 특정 그리디 규칙은 일반 최적 알고리즘이 아니다.
6. 활동 선택 예제
종료시간이 빠른 활동부터 정렬한 뒤 마지막 선택 활동과 겹치지 않는 활동을 고른다.
활동: A(1,4), B(3,5), C(0,6), D(5,7), E(8,9)
선택: A → D → E
시작시간이 빠른 활동, 길이가 짧은 활동을 먼저 고르는 규칙은 일반적으로 최적을 보장하지 않는다.
7. 0/1 배낭 DP
dp[i][w]를 앞의 i개 물건으로 용량 w에서 얻는 최대 가치라 하면 다음 점화식을 사용한다.
물건 i의 무게 wi, 가치 vi
wi > w: dp[i][w] = dp[i-1][w]
wi ≤ w: dp[i][w] = max(dp[i-1][w], dp[i-1][w-wi] + vi)
1차원 최적화에서는 용량을 큰 값에서 작은 값으로 순회해야 같은 물건을 한 번만 사용한다.
for weight, value in items:
for w in range(W, weight - 1, -1):
dp[w] = max(dp[w], dp[w - weight] + value)
작은 값부터 순회하면 갱신된 상태를 다시 사용하여 무제한 배낭처럼 동작할 수 있다.
8. LCS 상태와 점화식
dp[i][j]를 문자열 X의 앞 i글자와 Y의 앞 j글자의 LCS 길이라 한다.
X[i-1] == Y[j-1]: dp[i][j] = dp[i-1][j-1] + 1
다름: dp[i][j] = max(dp[i-1][j], dp[i][j-1])
부분문자열은 연속해야 하지만 부분수열은 연속하지 않아도 순서만 유지한다.
9. 메모리·시간 설계
DP 상태 수 × 상태당 전이 비용으로 시간복잡도를 계산한다. 상태가 nW개인 배낭 DP는 W가 입력 값의 크기라 의사다항시간일 수 있다. W의 비트 길이에 대한 다항시간과 구분한다.
10. 그리디·DP 선택 절차
1) 선택을 되돌리지 않아도 최적인 구조인가?
2) 교환 논증이나 컷 속성을 증명할 수 있는가?
3) 반례가 있다면 상태를 정의해 여러 선택을 비교할 수 있는가?
4) 중복 부분문제가 있는가?
5) 상태 수와 메모리가 허용되는가?
11. 대표 구분
- 분할 가능 배낭: 가치/무게 비율 그리디가 최적
- 0/1 배낭: 비율 그리디는 반례가 있으며 DP 사용
- Dijkstra: 음수 간선이 없을 때 탐욕적 확정이 안전
- Bellman-Ford: 음수 간선에서는 반복 완화 필요
최종 확인 문제
- 0/1 배낭 1차원 DP에서 용량을 큰 값부터 순회하는 이유를 설명하시오.
- 문자열
ABCBDAB와BDCABA의 LCS 길이는 4이다. 부분수열과 부분문자열의 차이를 설명하시오. - 분할 가능 배낭과 0/1 배낭에서 가치/무게 비율 그리디의 최적성 차이를 쓰시오.
- 그리디 알고리즘의 정당성을 보이는 대표 증명 방법 두 가지를 쓰시오.
- 배낭 DP O(nW)가 입력 비트 길이 관점에서 의사다항시간일 수 있는 이유를 설명하시오.