728x90
반응형
DP(Dynamic Programming)란?
DP는 문제를 작은 부분 문제(subproblems)로 나누어 해결하고, 그 결과를 저장하여 동일한 문제를 다시 계산하지 않도록 하는 알고리즘 설계 기법입니다. 중복되는 계산을 줄여 효율성을 극대화하는 것이 핵심입니다.
언제 DP를 사용할까?
DP는 문제를 해결하는 데 다음 두 가지 조건이 만족될 때 사용합니다:
- Overlapping Subproblems (겹치는 부분 문제):
- 동일한 부분 문제가 여러 번 반복해서 계산됩니다.
- 예를 들어 피보나치 수열 계산 시, 동일한 피보나치 값이 반복적으로 계산됨.
- Optimal Substructure (최적 부분 구조):
- 문제의 최적 해답이 부분 문제의 최적 해답으로 구성됩니다.
- 즉, 작은 문제들의 최적 결과를 합쳐서 전체 문제의 최적 결과를 구할 수 있습니다.
DP의 핵심 요소
- 점화식(Recurrence Relation):
- 큰 문제를 작은 문제로 나누는 규칙.
- 예를 들어, 피보나치 수열의 점화식: F(n) = F(n−1) + F(n−2)
- Memoization:
- 재귀 호출로 문제를 해결하며, 이미 계산한 결과를 저장.
- "탑다운(Top-Down)" 방식.
- Tabulation:
- 반복문으로 문제를 해결하며, 작은 문제부터 차례로 계산.
- "바텀업(Bottom-Up)" 방식.
DP의 두 가지 구현 방식
1. 탑다운 방식 (Top-Down)
- 큰 문제를 해결하기 위해 재귀 호출로 작은 문제를 계산.
- 작은 문제의 결과를 저장(메모이제이션).
- 예제: 피보나치 수열
def fib(n, memo):
if n in memo:
return memo[n]
if n <= 2:
return 1
memo[n] = fib(n-1, memo) + fib(n-2, memo)
return memo[n]
n = 10
print(fib(n, {})) # 55
2. 바텀업 방식 (Bottom-Up)
- 작은 문제부터 차례로 계산하여 큰 문제를 해결.
- 반복문을 사용하며, 메모이제이션 테이블을 직접 채움.
- 예제: 피보나치 수열
def fib(n):
dp = [0] * (n+1)
dp[1] = dp[2] = 1
for i in range(3, n+1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
n = 10
print(fib(n)) # 55
DP 적용 단계
- 문제를 작은 문제로 나눌 수 있는지 확인:
- 큰 문제를 작은 문제로 나눠서 해결 가능한지 확인합니다.
- 점화식 정의:
- 작은 문제의 해답을 어떻게 결합해 큰 문제를 해결할지 정의합니다.
- 메모이제이션 또는 테이블 정의:
- 부분 문제의 결과를 저장할 자료구조(리스트, 딕셔너리 등)를 정의합니다.
- 초기값 설정:
- 가장 작은 문제(기저 조건)의 해답을 설정합니다.
- 탑다운 또는 바텀업 방식으로 문제 해결.
DP 예제: 계단 오르기 문제
문제
- n개의 계단이 있고, 한 번에 1칸 또는 2칸씩만 오를 수 있다.
- 계단을 오르는 방법의 수를 구하라.
풀이
- 문제를 작은 문제로 나눌 수 있음:
- 계단을 오르는 방법은 마지막에 1칸을 오르거나, 2칸을 오른 경우의 합.
- 점화식:
- dp[n] = dp[n−1] + dp[n−2]
- 초기값:
- dp[1] = 1, dp[2] = 2
바텀업 구현
def climb_stairs(n):
dp = [0] * (n+1)
dp[1], dp[2] = 1, 2
for i in range(3, n+1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
print(climb_stairs(5)) # 8
DP의 시간 복잡도
- DP는 부분 문제의 수 × 각 부분 문제를 푸는 데 걸리는 시간으로 시간 복잡도가 결정됩니다.
- 예를 들어, 피보나치 수열:
- 부분 문제 수: O(n)
- 각 문제 계산 시간: O(1)
- 총 시간 복잡도: O(n)
DP의 장점
- 중복 계산을 제거하여 효율성을 극대화.
- 복잡한 문제를 작은 문제로 나눠 해결 가능.
- 최적의 해를 보장.
DP 문제를 풀 때 기억해야 할 것
- 문제를 작은 문제로 나눌 수 있는지 확인.
- 점화식(재귀적 관계)을 정의.
- 작은 문제의 해답을 저장해 중복 계산 방지.
- 탑다운 또는 바텀업 방식 중 하나 선택.
'Category > Algorithm' 카테고리의 다른 글
| 우선순위 큐(Priority Queue)와 코딩 테스트 활용법 (3) | 2024.11.28 |
|---|---|
| 최장 증가 부분 수열(LIS, Longest Increasing Subsequence) (0) | 2024.11.28 |
| 파라메트릭 서치(Parametric Search)란? (0) | 2024.11.28 |
| 다익스트라 알고리즘(Dijkstra's Algorithm) (0) | 2024.11.22 |
| 백트래킹(Backtracking) (0) | 2024.11.21 |