- Top-Down : 재귀 함수 사용. 큰 문제를 해결하기 위해 작은 문제를 재귀적으로 호출해 해결한 후, 큰 문제의 답을 얻을 수 있도록
ex) Memorization 방식- Bottom-Up : DP의 전형적인 형태로 리스트와 반복문 사용.
작은 문제를 하나씩 해결해가며 리스트에 저장해 다음 문제를 해결
1. 최적 부분 구조(Optimal Substructure)
큰 문제를 작은 문제로 나눌 수 있으며, 작은 문제의 답을 모아 큰 문제를 해결할 수 있음
2. 중복되는 부분 문제(Overlapping Subproblem)
동일한 작은 문제를 반복적으로 해결
ex) 피보나치 수열
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89
피보나치 수열을 아래와 같은 점화식으로 표현 가능

이러한 수열을 배열이나 리스트로 표현할 수 있다.
피보나치 수열의 수학적 점화식을 프로그래밍으로 표현할 때 재귀를 쓰면 간단하다.
def fibo(x):
if x == 1 or x == 2: # 재귀를 멈출 수 있는 조건
return 1
return fibo(x - 1) + fibo(x - 2)
그러나 x가 커질수록 수행시간이 계속 증가한다. 일반적으로 O()의 지수시간이 소요된다.
예를 들어, fibo(6)을 해결하기 위해 fibo(2)가 여러번 호출된다.
=> 중복적으로 문제를 풀이하고 있음. 피보나치 수열은 위의 2가지 조건을 모두 만족하기 때문에 DP방식으로 해결 가능하다.
# 한 번 계산된 결과를 메모이제이션하기 위한 리스트 초기화
# 99번째의 fibo를 구하기 위해 길이를 100으로 설정
d = [0] * 100
# 피보나치 함수를 재귀함수로 구현(하향식)
def fibo(x):
# 종료 조건
if x == 1 or x == 2:
return 1
# 이미 계산한 적 있는 문제라면 그대로 반환
if d[x] != 0:
return d[x]
# 아직 계산하지 않은 문제라면, 점화식에 따라 피보나치 결과 반환
d[x] = fibo(x-1) + fibo(x-2)
return d[x]
print(fibo(99))
# DP 테이블 초기화
d = [0] * 100
# 첫 번째 피보나치 수와 두 번째 피보나치 수는 1
d[1] = d[2] = 1
n = 99
# 피보나치 함수를 반복문으로 구현
for i in range(3, n + 1):
d[i] = d[i-1] + d[i-2]
print(d[n])
| DP | 분할 정복 | |
|---|---|---|
| 공통점 | 모두 최적 부분 구조를 가질 때 사용 가능 | 모두 최적 부분 구조를 가질 때 사용 가능 |
| 차이점 | 각 부분의 문제들이 서로 영향을 미치며 부분 문제가 중복됨 | 동일한 부분 문제가 반복적으로 계산되지 않음 |