관리 메뉴

kimgusxo 님의 블로그

다이나믹 프로그래밍(Dynamic Programming) 본문

Algorithm

다이나믹 프로그래밍(Dynamic Programming)

kimgusxo 2025. 5. 5. 21:17

1. DP(Dynamic Programming)이란?

- 다이나믹 프로그래밍은 복잡한 문제를 더 작은 하위 문제로 나누어 해결하고 그 결과를 중복해서 다시 계산하지 않도록 저장하거나 차례차례 결과를 쌓아올리는 기법이다.

- 분할정복과 비슷하지만 하위 문제가 중복되어 등장할 때 중복 계산을 제거하는것이 차이점이다.

 

1-1. 핵심 아이디어

- 중복 부분 문제(Overlapping Subproblems): 동일한 하위 문제가 여러 번 등장하면 한 번 계산한 값을 저장해놨다가 재사용

- 최적 부분 구조(Optimal Substructure): 문제의 최적 해가 하위 문제의 최적해들로 만들어져야 함

 

1-2. 장단점

- 장점: 중복 계산을 제거하여 시간복잡도가 감소, 단계별 구현 패턴이 일정

- 단점: 상태 저장용 메모리(배열, 맵 등) 필요, 점화식 설계에 난이도가 있음

 

2. 구현 방식

2-1. Top Down(메모이제이션)

- 재귀를 호출하면서 결과를 맵이나 배열에 저장

- 코드 구조가 직관적이다.

// 피보나치 예제(Top Down)

public class FiboTopDown {

    static Map<Integer, Long> memo = new HashMap<>();

    public static long fibo(int n) {
        if(n <= 1) {
            return n;
        }

        if(memo.containsKey(n)) {
            return memo.get(n);
        }

        long res = fibo(n-1) + fibo(n-2);
        memo.put(n, res);
        return res;
    }

    public static void main(String[] args) {
        // 출력: 12586269025
        System.out.println(fibo(50));
    }
}

 

 

2-2. BottomUp(타뷸레이션)

- 작은 하위문제부터 반복문으로 차례차례 결과 채우기

- 스택 오버플로우 걱정 없고 공각 최적화 기법을 적용하기 쉽다

// 피보나치 예제(Bottom Up

public class FiboBottomUp {
    public static long fibo(int n) {
        if(n <= 1) {
            return n;
        }

        long[] dp = new long[n+1];
        dp[0] = 0;
        dp[1] = 1;

        for(int i = 2; i <= n; i++) {
            dp[i] = dp[i-2] + dp[i-1];
        }
        return dp[n];
    }

    public static void main(String[] args) {
        // 출력: 12586269025
        System.out.println(fib(50));
    }
}

 

3. DP 활용 문제

3-1. 계단오르기

- 한 계단에 1칸 또는 2칸씩 오를 수 있을 때 n개의 계단을 오르는 방법의 수

- 점화식: dp[i] = dp[i-1] + dp[i-2]

public class ClimbStairs {
    public static int climb(int n) {
        if(n <= 1) {
            return 1;
        }

        int[] dp = new int[n+1];
        dp[0] = 0;
        dp[1] = 1;

        for (int i = 2; i <= n; i++) {
            dp[i] = dp[i-1] + dp[i-2];
        }

        return dp[n];
    }

    public static void main(String[] args) {
        // 출력: 8
        System.out.println(climb(5));
    }
}

 

3-2. 동전 교환

- 다양한 동전이 주어질 때 합이 target이 되도록 만드는 모든 경우의 수

- 점화식: for each coin c: dp[i] += dp[i-c];

public class CoinChargeCount {
    public static int cointCharge(int[] coins, int target) {
        int[] dp = new int[target+1];
        dp[0] = 1;

        for(int c : coins) {
            for (int i = c; i <= target; i++) {
                dp[i] += dp[i-c];
            }
        }

        return dp[target];
    }

    public static void main(String[] args) {
        // 예제 데이터
        int[] coins = {1, 2, 5};

        // 출력: 11
        System.out.println(coinChange(coins, 11));
    }
}

'Algorithm' 카테고리의 다른 글

비트마스킹(Bitmasking)  (0) 2025.04.28
분할정복(Divide and Conquer)  (0) 2025.04.08
백트래킹(Backtracking)  (0) 2025.03.28
깊이 우선 탐색(DFS)  (0) 2025.03.22
너비 우선 탐색(BFS)  (0) 2025.03.22