본문 바로가기
알고리즘

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

by 쪼꼬에몽 2024. 3. 26.

동적 계획법(Dynamic Programming, DP)


- 큰 문제를 작은 문제로 나누어서 푸는 방식의 알고리즘.

- 처음 문제를 더 작은 문제로 나누고 결과를 알아내어 저장함. 이것들을 이용해 원래 답을 계산함.

 

성립 조건


1. 최적 부분 구조(Optimal Substructure)

- 상위 문제를 하위 문제로 나누어 하위 문제의 답으로 상위 문제 해결 가능.

2. 중복되는 부분 문제(Overlapping Subproblem)

- 문제를 여러 개의 부분 문제로 쪼갤 수 있음.

 

ex) 피보나치 수열: 0 1 1 2 3 5 8 13 21 34 55 89 ...

  • F0 = 0
  • F1 = 1
  • Fn = Fn-1 + Fn-2 (n ≥ 2)

큰 문제는 작은 문제 통해 해결할 수 있음.

 

n번째 피보나치 수 = n - 1번째 피보나치 수 + n - 2번째 피보나치 수

n - 1번째 피보나치 수 = n - 2번째 피보나치 수 + n - 3번째 피보나치 수

n - 2번째 피보나치 수 = n - 3번째 피보나치 수 + n - 4번째 피보나치 수 ...

 

겹치는 부분 문제는 답이 항상 같음. 정답을 캐시에 저장하는 것을 메모이제이션(Memoization).

 

메모제이션 Memoization


- 계산 결과를 메모리 공간에 저장함.

- 동일 문제를 호출하면 저장 결과를 그대로 가져옴.

- 캐싱(Caching): 값을 기록

 

피보나치 수를 구하는 함수

int fibonacci(int n) {
    // f(0) = 0, f(1) = 1
    if (n <= 1) {   
        return n;
    } else {
        return fibonacci(n-1) + fibonacci(n-2);
    }
}

 

중복된 계산을 반복해서 하게 됨. 시간복잡도 O(2^n).

 

메모이제이션을 통해 모든 문제를 한 번만 품. 중복되는 부분문제 결과 생략.

 

하위 문제의 값을 memo[]에 저장.

int memo[1000];
int fibonacci(int n) {
    if (n <= 1) {
        return n;
    } else {
        // memo의 값이 0이 아니면 그 값을 그대로 사용
        if (memo[n] != 0) {
            return memo[n];
        }
        memo[n] = fibonacci(n-1) + fibonacci(n-2);
        return memo[n];
    }
}

 

Top-down 과 Bottom-up


Top-down (하향식)

- 하위 문제를 재귀적으로 호출해 이를 해결함으로써 상위 문제 해결

- 하위 문제를 저장하기 위해 메모이제이션 사용

public class Main {
    static int[] memo;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        memo  = new int[n+1];

        System.out.println(fibonacci(n));
    }

    // Top-down
    static int fibonacci(int x) {
        if (x <= 1) {
            return x;
        }else {
            if (memo[x] != 0)
                return memo[x];
            
            memo[x] = fibonacci(x - 1) + fibonacci(x - 2);
            return memo[x];
        }
    }
}

Bottom-up (상향식)

- 하위 문제를 해결하면서 나온 값으로 상위 문제 해결

- DP 형태 

- 문제 결과 값 저장용 리스트 = DP 테이블

- 반복문 사용

public class Main {
    static int[] memo;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        memo  = new int[n+1];

        System.out.println(fibonacci(n));
    }

    // Bottom-up
    static int fibonacci(int x) {
    	memo[0] = 0;
        memo[1] = memo[2] =1;
        
        for(int i=3; i<x+1; i++) {
            memo[i] = memo[i-1] + memo[i-2];
        }
        return memo[x];
    }
}

 

'알고리즘' 카테고리의 다른 글

[알고리즘] 최장 증가 부분 수열 (LIS)  (0) 2024.03.28