Home [알고리즘] 동적 계획법(DP: Dynamic Programming)
Post
Cancel

[알고리즘] 동적 계획법(DP: Dynamic Programming)

동적 계획법(DP) 알고리즘에 대해 공부 후 정리하였습니다.

동적 계획법(DP: Dynamic Programming)

1️⃣ DP란?

큰 문제를 작은 문제로 나누고, 중복되는 작은 문제를 한 번만 풀어서
그 결과를 저장해두고 재사용하여 전체 문제를 해결하는 기법입니다.

2️⃣ DP의 핵심 조건 (2가지)

  • 중복 부분 문제 (Overlapping Subproblems) : 같은 계산을 여러 번 반복하는 문제
  • 최적 부분 구조 (Optimal Substructure) : 큰 문제의 최적해가 작은 문제의 최적해로 구성됨

3️⃣ DP의 시간 복잡도

DP의 시간 복잡도는 일반적으로 O(상태 개수 × 상태 전이 계산 시간) 으로 계산됩니다.

  • 피보나치 (Top-down / Bottom-up): 상태 개수 = n, 전이 시간 = O(1) → O(n)
  • LCS (Longest Common Subsequence): 상태 개수 = n × m, 전이 시간 = O(1) → O(nm)
  • 배낭 문제: 상태 개수 = n × W, 전이 시간 = O(1) → O(nW)

✅ DP 시간 복잡도 계산 팁

  1. 배열 크기 = 상태 개수
  2. 한 상태에서 다음 상태로 가는 경우의 수 = 전이 계산 시간
  3. 곱하면 총 시간 복잡도

4️⃣ DP 유형 분류

  • 시간 복잡도는 동일함
유형설명실제 실행 시간효과적인 예시예시
Top-Down재귀 + 메모이제이션느림테이블의 일부만 채워야 할 때, 테이블을 채우는 순서가 복잡할 때f(n) = f(n-1) + f(n-2)
Bottom-Up반복문 + 배열빠름테이블을 채우는 순서가 직관적으로 보일 때dp[i] = dp[i-1] + dp[i-2]

5️⃣ DP 문제 풀이 팁

  1. 테이블 정하기 (ex: dp[i] = i를 1로 만드는 최소 연산 횟수)
  2. 점화식 구하기 (ex: dp[i] = dp[i-1] + dp[i-2])
  3. 초기값 정하기 (ex: dp[0], dp[1])
  4. 메모이제이션 or 반복문 선택
    • 재귀(Top-down): 간단한 구조, but 느릴 수 있음
    • 반복문(Bottom-up): 빠르고 안전, but 구현이 복잡할 수 있음
  5. 작은 입력부터 차례로 채우기 (ex: dp[2], dp[3], …, dp[n] 순서로)

Prefix Sum (누적 합)

1️⃣ 개념

배열의 부분합을 빠르게 구하기 위해, 미리 합을 구해놓은 배열을 만드는 기법
구간 합 쿼리를 O(1)에 처리 가능

2️⃣ 특징

  • 배열 변경이 없는 경우 매우 효율적 (변경이 있으면 Fenwick Tree, Segment Tree 고려)
  • 1D, 2D, 3D 등 다차원 배열에도 적용 가능

3️⃣ 시간 복잡도

  • 전처리: O(n)
  • 구간합 질의: O(1)

4️⃣ 구현 방법

  1. prefix[i] = arr[0] ~ arr[i] 합
  2. 구간합 (l, r) = prefix[r] - prefix[l-1]

5️⃣ 예시 코드 (1D)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class PrefixSumExample {
    public static void main(String[] args) {
        int[] arr = {2, 4, 5, 7, 1};
        int n = arr.length;
        int[] prefix = new int[n + 1]; // 1-based indexing

        // 전처리
        for (int i = 1; i <= n; i++) {
            prefix[i] = prefix[i - 1] + arr[i - 1];
        }

        // 구간합 예시: arr[1..3] (0-indexed: 4+5+7)
        int l = 2, r = 4; // 1-based index
        int sum = prefix[r] - prefix[l - 1];
        System.out.println("구간합: " + sum); // 출력: 13
    }
}
This post is licensed under CC BY 4.0 by the author.