수학적 최적화 방법이자 컴퓨터 프로그래밍 기법
복잡한 문제를 작은 하위 문제로 나누고, 각 하위 문제를 한 번만 계산한 후 결과를 메모리에 저장하여 효율 UP
분할 정복(Divide and Conquer)과 유사하지만, 중복 계산을 피하기 위해 재귀(recursion)를 사용하지 않거나 최소화하는 것이 특징
1) Overlapping Sub-problems (중복되는 하위 문제)
2) Optimal Substructure (최적 부분 구조)
Tabulation (Bottom-Up) : 반복문 사용
// Bottom-Up 방식 (Tabulation)
public static long bottomUp(int n) {
for (int i = lastFibIndex + 1; i <= n; i++)
fib[i] = fib[i - 1] + fib[i - 2];
if (n > lastFibIndex) lastFibIndex = n;
return fib[n];
}
Memoization (Top-Down) : 재귀 + 캐싱
// Top-Down 방식 (Memoization)
public static long topDown(int n) {
if (n < lastFibIndex) return fib[n];
fib[n] = topDown(n-2) + topDown(n-1);
lastFibIndex = n;
return fib[n];
}
Step 1: DP 문제인지 확인
Step 2: 상태(state) 정의
Step 3: 상태 간 관계식 수립
Step 4: 구현 (Top-Down 또는 Bottom-Up)
Given an integer array of coins[] of size n representing different types of denominations and an integer sum, the task is to count all combinations of coins to make a given value sum.
Note: Assume that you have an infinite supply of each type of coin.
Examples 1)
Input: sum = 4, coins[] = [1, 2, 3]
Output: 4
Explanation: There are four solutions: [1, 1, 1, 1], [1, 1, 2], [2, 2] and [1, 3]
Examples 2)
Input: sum = 10, coins[] = [2, 5, 3, 6]
Output: 5
Explanation: There are five solutions:
[2, 2, 2, 2, 2], [2, 2, 3, 3], [2, 2, 6], [2, 3, 5] and [5, 5]
점화식 : dp[i] += dp[i - coin]
class Itm {
static int count(int[] coins, int sum) {
int n = coins.length;
int[] dp = new int[sum + 1];
dp[0] = 1;
for (int coin : c) {
for (int amount = coin; amount <= n; amount++) {
dp[amount] += dp[amount - coin];
}
}
return dp[sum];
}
public static void main(String[] args) {
int[] coins = {1, 2, 3};
int sum = 5;
System.out.println(count(coins, sum));
}
}
기본 문제 (0/1 Knapsack):
Example
물건 3개: weights = [1, 3, 4], values = [15, 20, 30]
배낭 최대 무게: W = 4
dp[i][w] = i번째까지의 물건으로, 무게 w를 넘지 않게 담았을 때의 최대 가치
if (weight[i-1] <= w)
dp[i][w] = max(dp[i-1][w], // 안 담음
value[i-1] + dp[i-1][w - weight[i-1]]) // 담음
else
dp[i][w] = dp[i-1][w]; // 담을 수 없음