개념
동적 계획법(DP)은 부분 문제의 답을 저장해 두고 다시 필요할 때 사용하는 방법입니다. 피보나치 수열을 통해 같은 값을 반복해서 계산하는 재귀 풀이를 어떻게 바꿀 수 있는지 살펴봅니다.
피보나치 수열과 상태
피보나치 수열은 , 로 시작하며, 이후의 항은 바로 앞의 두 항을 더한 값입니다.
| i | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| 0 | 1 | 1 | 2 | 3 | 5 | 8 |
DP에서 상태는 저장할 값이 어떤 문제의 답인지를 나타냅니다. 여기서는 를 피보나치 수열의 번째 항으로 정의합니다. 그러면 초기값은 , 이고, 이전 상태로 다음 상태를 구하는 전이식은 입니다.
단순 재귀에서 반복되는 계산
정의를 그대로 재귀에 적용하면 과 은 즉시 반환하고, 나머지는 앞의 두 항을 재귀로 구해 더합니다. 를 계산하는 첫 호출은 와 으로 나뉩니다.
| 호출 | 값을 구하기 위해 호출하는 항 |
|---|---|
| , | |
| , | |
| , | |
| , |
에서 직접 호출한 과 를 구할 때 호출한 은 같은 계산입니다. 두 호출은 각각 와 을 구하고, 그 안에서도 같은 계산을 반복합니다. 실제로 를 구할 때 은 번, 는 번 호출됩니다. 필요한 항은 부터 까지 개이지만, 함수 호출은 종료 조건에 도달하는 호출까지 합쳐 번 발생합니다.
메모이제이션: 계산한 항을 재사용
Top-down DP는 재귀 호출에서 구한 결과를 저장하는 방식입니다. 먼저 해당 항이 저장되어 있는지 확인하고, 저장되어 있다면 그 값을 반환합니다. 처음 구하는 항이면 앞의 두 항을 구해 더한 뒤 저장합니다. 계산 결과를 기억해 두는 것을 메모이제이션이라고 합니다.
를 구하며 안에서 를 계산해 저장했다면, 이후 가 다시 을 요청할 때는 저장된 를 반환합니다. 그 아래의 와 계산도 다시 수행할 필요가 없습니다. 각 항의 덧셈은 한 번씩만 수행합니다.
계산 전 상태를 로 표시하면 실제 결과인 과 구별할 수 있습니다. 재귀 종료 조건인 과 은 미리 저장합니다.
반복문: 작은 항부터 계산
Bottom-up DP는 필요한 작은 상태부터 순서대로 채우는 방식입니다. 과 을 먼저 저장하고 부터 까지 증가시키며 를 계산합니다.
를 구할 때 참조하는 과 는 이미 계산되어 있습니다. 계산 순서를 직접 정하므로 재귀 호출이 필요하지 않습니다. 만 필요하면 직전 두 항만 보관하여 저장 공간을 줄일 수도 있습니다.
복잡도 비교
구하려는 항의 번호를 이라고 합니다. 단순 재귀는 한 호출이 최대 두 호출로 나뉘므로 시간복잡도의 상한을 으로 잡을 수 있습니다. 동시에 쌓이는 재귀 호출의 깊이는 입니다.
| 방법 | 시간복잡도 | 공간복잡도 |
|---|---|---|
| 단순 재귀 | 상한 | 호출 스택 |
| 메모이제이션 | 저장 배열·호출 스택 | |
| 배열을 채우는 반복문 | 저장 배열 | |
| 직전 두 항만 저장하는 반복문 |
DP의 시간은 상태 수와 각 상태를 계산하는 데 필요한 연산 수로 구합니다. 피보나치 수열은 상태가 개이고, 초기값 이후의 상태마다 덧셈 한 번을 수행하므로 입니다.
예제
문제
피보나치 수열은 , 이며, 일 때 로 정의된다. 을 구하여라.
입력
첫째 줄에 음이 아닌 정수 이 주어진다. 이다.
출력
을 출력한다.