SW 전공

SW 전공 이론 학습

이론 목록으로 돌아가기

그리디와 동적 계획법

그리디 선택과 동적 계획법의 상태·점화식을 비교하고 적용 조건을 판단한다.

예상 읽기 5

1. 그리디 알고리즘

그리디 알고리즘은 각 단계에서 현재 가장 좋아 보이는 선택을 하고 이전 선택을 되돌리지 않는다. 구현은 단순하고 빠른 경우가 많지만, 모든 문제에서 최적해를 보장하지 않는다.

최적해를 보장하려면 문제에 다음 성질이 필요하다.

  • 그리디 선택 속성: 현재의 최적 선택을 포함하는 전체 최적해가 존재한다.
  • 최적 부분구조: 전체 최적해가 부분 문제의 최적해로 구성된다.

대표 예는 활동 선택, 최소신장트리의 Kruskal·Prim, 음수가 없는 최단경로의 Dijkstra, 특정 화폐 체계의 동전 선택이다.

동전이 1, 5, 10, 50이라면 큰 동전부터 선택해도 최소 개수가 되지만, 동전이 1, 3, 4이고 금액이 6이면 그리디는 4+1+1의 3개를 고르는 반면 최적은 3+3의 2개이다.

2. 동적 계획법(DP)

동적 계획법은 같은 부분 문제가 반복되고 최적 부분구조가 있을 때 결과를 저장하여 중복 계산을 제거한다.

구성 절차

  1. 상태 정의: dp[i] 또는 dp[i][j]가 무엇을 뜻하는지 정한다.
  2. 점화식: 작은 상태에서 큰 상태를 계산하는 관계를 세운다.
  3. 초기값: 가장 작은 문제의 답을 지정한다.
  4. 계산 순서: 필요한 선행 상태가 먼저 계산되도록 한다.
  5. 정답 위치: 최종 답이 어느 상태에 있는지 확인한다.

메모이제이션과 테이블 방식

  • 메모이제이션: 재귀적으로 필요한 상태만 계산해 저장한다.
  • 테이블 방식: 작은 상태부터 반복문으로 채운다.

피보나치 수를 단순 재귀로 계산하면 같은 값을 반복 계산하지만, DP를 사용하면 각 값을 한 번만 계산해 O(n)에 해결할 수 있다.

3. 대표 DP 문제

  • 최소 동전 수
  • 0/1 배낭 문제
  • 최장 공통 부분수열(LCS)
  • 격자 경로 수
  • 편집 거리

상태 의미와 점화식이 올바른지, 반복 순서가 맞는지를 함께 검증한다.

4. 비교

항목그리디동적 계획법
선택현재 최선 선택을 확정여러 선택 결과를 비교
이전 선택 수정보통 하지 않음저장된 상태를 통해 비교
필요 성질그리디 선택 속성중복 부분문제·최적 부분구조
장점빠르고 단순더 넓은 최적화 문제 해결
주의반례 확인 필요상태 수와 메모리 증가

5. 그리디 정당성 증명

그리디는 “지금 가장 좋아 보인다”만으로 충분하지 않다. 대표 증명 방식은 다음과 같다.

  • 교환 논증: 어떤 최적해의 첫 선택을 그리디 선택으로 바꾸어도 최적성을 잃지 않음을 보임
  • 앞서가기 논증: 매 단계까지 그리디 해가 다른 해보다 뒤처지지 않음을 보임
  • 컷 속성: MST에서 어떤 컷을 가로지르는 최소 가중치 간선이 안전함을 사용

반례 하나만 있어도 특정 그리디 규칙은 일반 최적 알고리즘이 아니다.

6. 활동 선택 예제

종료시간이 빠른 활동부터 정렬한 뒤 마지막 선택 활동과 겹치지 않는 활동을 고른다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
활동: 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에서 얻는 최대 가치라 하면 다음 점화식을 사용한다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
물건 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차원 최적화에서는 용량을 큰 값에서 작은 값으로 순회해야 같은 물건을 한 번만 사용한다.

PYTHON코드 영역 안에서 좌우로 이동할 수 있습니다.
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 길이라 한다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
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 선택 절차

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
1) 선택을 되돌리지 않아도 최적인 구조인가?
2) 교환 논증이나 컷 속성을 증명할 수 있는가?
3) 반례가 있다면 상태를 정의해 여러 선택을 비교할 수 있는가?
4) 중복 부분문제가 있는가?
5) 상태 수와 메모리가 허용되는가?

11. 대표 구분

  • 분할 가능 배낭: 가치/무게 비율 그리디가 최적
  • 0/1 배낭: 비율 그리디는 반례가 있으며 DP 사용
  • Dijkstra: 음수 간선이 없을 때 탐욕적 확정이 안전
  • Bellman-Ford: 음수 간선에서는 반복 완화 필요

최종 확인 문제

  1. 0/1 배낭 1차원 DP에서 용량을 큰 값부터 순회하는 이유를 설명하시오.
  2. 문자열 ABCBDABBDCABA의 LCS 길이는 4이다. 부분수열과 부분문자열의 차이를 설명하시오.
  3. 분할 가능 배낭과 0/1 배낭에서 가치/무게 비율 그리디의 최적성 차이를 쓰시오.
  4. 그리디 알고리즘의 정당성을 보이는 대표 증명 방법 두 가지를 쓰시오.
  5. 배낭 DP O(nW)가 입력 비트 길이 관점에서 의사다항시간일 수 있는 이유를 설명하시오.