다이나믹 프로그래밍은 메모리를 적절히 사용하여 수행 시간 효율성을 비약적으로 향상시키는 방법
이미 계산된 결과(작은 문제)는 별도의 메모리 영역에 저장하여 다시 계산하지 않도록 합니다.
다이나믹 프로그래밍의 구현은 일반적으로 두 가지 방식(탑다운과 보텀업)으로 구성
다이나믹 프로그래밍은 동적 계획법이라고 부릅니다.
일반적인 프로그래밍 분야에서의 동적이란
자료구조에서 동적 할당은 '프로그램이 실행되는 도중에 실행에 필요한 메모리를 할당하는 기법'을 의미
반면에 다이나믹 프로그래밍에서 '다이나믹'은 별다른 의미 없이 사용된 단어
다이나믹 프로그래밍은 문제가 다음의 조건을 만족할 때 사용할 수 있습니다.
1. 최적 부분 구조(Optimal Substructure)
큰 문제를 작은 문제로 나눌 수 있으며 작은 문제의 답을 모아서 큰 문제를 해결할 수 있습니다.
2. 중복되는 부분 문제(Overlapping Subproblem)
동일한 작은 문제를 반복적으로 해경해야 합니다.
다이나믹 프로그래밍으로 해결할 수 있는 문제



f(4) = f(3) + f(2) = f(2) + f(1) + f(2)
def fibo(x):
if x == 1 or x == 2:
return 1
return fibo(x - 1) + fibo(x - 2)
print(fibo(4))
# 3

중복되는 계산을 하게 됨으로 시간 복잡도는O(2^n)입니다.
그렇기 때문에 이미 해결한 문제는 계산하지 않도록 해야한다.
1. 최적 부분 구조: 큰 문제를 작은 문제로 나눌 수 있습니다.
2. 중복되는 부분 문제: 동일한 작은 문제를 반복적으로 해결
피보나치 수열은 다이나믹 프로그래밍의 사용 조건을 만족합니다.
방법
메모이제이션(Memoization) 탑다운
메모이제이션은 다이나믹 프로그래밍을 구현하는 방법 중 하나
한 번 계산한 결과를 메모리 공간에 메모하는 기법입니다.
같은 문제를 다시 호출하면 메모했던 결과를 그대로 가져옵니다.
값을 기록해 놓는다는 점에서 캐싱이라고도 합니다.
탑다운 VS 보텀업
탑다운(메모이제이션) 방식은 하향식이라고도 하며 보텀업 방식은 상향식이라고도 합니다.
다이나믹 프로그래밍의 전형적인 형태는 보텀업 방식입니다.
결과 저장용 리스트(배열) DP 테이블이라고 부릅니다.
엄격히 말하면 메모이제이션은 이전에 계산된 결과를 일시적으로 기록해 놓는 넓은 개념을 의미합니다.
따라서 메모이제이션은 다이나믹 프로그래밍에 국한된 개념은 아닙니다.
한 번 계산된 결과를 담아 놓기만 하고 다이나믹 프로그래밍을 활용하지 않을 수도 있습니다.
탑다운 방식
# 한 번 계산된 결과를 메모이제이션하기 위한 리스트 초기화
d = [0] * 100
# 피보나치 함수를 재귀함수로 구현(탑다운 다이나믹 프로그래밍)
def fibo(x):
#종료 조건(1 혹은 2일 때 1을 반환)
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] = 1
d[2] = 1
n = 99
# 피보나치 함수 반복문으로 구현
for i in range(3, n + 1):
d[i] = d[i - 1] + d[i - 2]
print(d[n])
작은 문제부터 해결 후 그 작은 문제들을 조합해서 큰 문제를 해결한다.
메모이제이션을 사용 했을 때 동작 분석


f(5)의 값을 구할때에는 f(4)와 f(3)을 구해야하는데 이 때 f(3)은 f(4)에 의해서 만들어지기 때문에 계산할 필요가 없어집니다.
다이나믹 프로그래밍 VS 분할 정복
다이나믹 프로그래밍과 분할 정복은 모두 최적 부분 구조를 가질 때 사용할 수 있습니다.
큰 문제를 작은 문제로 나눌 수 있으며 작은 문제의 답을 모아서 큰 문제를 해결할 수 있는 상황
다이나믹 프로그래밍과 분할 정복의 차이점은 부분 문제의 중복입니다.
다이나믹 프로그래밍 문제에서는 각 부분 문제들이 서로 영향을 미치며 부분 문제가 중복됩니다.
분할 정복 문제에서는 동일한 부분 문제가 반복적으로 계산되지 않습니다.
분할 정복의 대표적인 예 퀵 정렬

다시 말해 5를 피벗으로 설정 후 퀵 정렬을 1회 실행하면 다시는 5를 피벗으로 두지 않고 진행이 되기 때문에 부분 문제가 중복되지 않습니다.
다이나믹 프로그래밍 문제에 접근하는 방법
주어진 문제가 다이나믹 프로그래밍 유형임을 파악하는 것이 중요합니다.
가장 먼저 그리디. 구현, 완전 탐색 등의 아이디어로 문제를 해결할 수 있는지 검토할 수 있습니다.
다른 알고리즘으로 풀이 방법이 떠오르지 않으면 다이나믹 프로그래밍을 고려해 봅시다.
일단 재귀 함수로 비효율적인 완전 탐색 프로그램을 작성한 뒤에 (탑다운) 작은 문제에서 구한 답이 큰 문제에서 그대로 사용될 수 있으면 코드를 개선하는 방법을 사용할 수 있습니다.
일반적인 코딩 테스트 수준에서는 기본 유형의 다이나믹 프로그래밍 문제가 출제되는 경우가 많습니다.



해결 아이디어
예시를 확인해 봅시다. N = 4일 때, 다음과 같은 경우들이 존재할 수 있습니다.
식량을 선택할 수 있는 경우의 수는 다음과 같이 8가지
7번째 경우에서 8만큼의 식량을 얻을 수 있으므로 최적의 해는 8입니다.
ai = i 번째 식량창고까지의 최적의 해(얻을 수 있는 식량의 최댓값)
이렇게 정의한다면 다이나믹 프로그래밍을 적용할 수 있습니다.
'코딩테스트를 위한 자료구조 알고리즘' 카테고리의 다른 글
| 자료구조 브라우저 뒤로 가기 앞으로 가기 (0) | 2021.10.30 |
|---|---|
| 최단 경로 문제 (0) | 2021.10.29 |
| 이진 탐색(Binary Search) (0) | 2021.10.26 |
| 정렬 알고리즘 비교 (0) | 2021.10.26 |
| 계수 정렬 (0) | 2021.10.26 |