문제가 다음의 조건을 만족할 때 동적 계획법 사용 가능
최적 부분 구조 (Optimal Substructure)
중복되는 부분 문제 (Overlapping Subproblem)
탑다운 방식은 하향식이라고도 하며 바텀업 방식은 상향식이라고도 함
동적 계획법의 전형적인 형태는 바텀업 방식
동적 계획법에서 결과 저장용 리스트는 DP 테이블이라고 부름
엄밀히 말하면 메모이제이션은 이전에 계산된 결과를 일시적으로 기록해 놓은 넓은 개념
메모이제이션은 동적 계획법에 국한된 개념이 아님
한 번 계산된 결과를 담아 놓기만 하고 동적 계획법을 위해 활용하지 않을 수도 있음
위의 [프로그래머스] 정수 삼각형 문제는 전형적인 DP 문제다.
이 문제를 탑다운 방식과 바텀업 방식으로 각각 구현해 보겠다.
우선 탑다운 방식으로 푼다면 아래와 같이 구현할 수 있다.
function solution(triangle) {
let n = triangle.length;
let memo = Array.from(Array(n), () => Array(n).fill(-1));
function dfs(x, y) {
// 삼각형의 바닥 행에 도착한 경우
if (x === n - 1) return triangle[x][y];
// 이전에 계산된 적이 있는 경우
if (memo[x][y] !== -1) return memo[x][y];
let left = dfs(x + 1, y);
let right = dfs(x + 1, y + 1);
// 현재 위치의 최대 합 계산
memo[x][y] = triangle[x][y] + Math.max(left, right);
return memo[x][y];
}
return dfs(0, 0);
}
탑다운 방식을 사용하면 현재 위치에서 시작해서 삼각형의 바닥까지 내려갈 때의 최대 합을 재귀적으로 계산하고, 중복 계산을 피하기 위해 계산된 값을 메모이제이션 테이블에 저장하는 방식으로 해결할 수 있다.
바텀업 방식으로 푼다면 아래와 같이 구현할 수 있다.
function solution(triangle) {
let dp = triangle.slice(); // triangle 복사본
// 삼각형의 바닥 바로 위 행부터 첫 번째 행까지 순회
for (let i = triangle.length - 2; i > -1; i--) {
for (let j = 0; j < dp[i].length; j++) {
// 최대 합 계산
dp[i][j] += Math.max(dp[i + 1][j], dp[i + 1][j + 1]);
}
}
return dp[0][0];
}
바텁업 방식을 사용하면 삼각형 바닥부터 시작해서 꼭대기까지 각 위치에서 가능한 최대 합을 계산하고, 그 결과를 dp
배열에 저장하여 문제를 해결할 수 있다.
주어진 문제가 동적 계획법 유형임을 파악하는 것이 중요
가장 먼저 그리디, 구현, 완전 탐색 등의 아이디어로 문제를 해결할 수 있는지 검토한 후, 다른 알고리즘으로 풀이 방법이 떠오르지 않으면 동적 계획법을 고려
일단 재귀 함수로 비효율적인 완전 탐색 프로그램을 작성한 뒤에(탑다운) 작은 문제에서 구한 답이 큰 문제에 그대로 사용될 수 있으면, 코드를 개선하는 방법 사용 가능
일반적인 코딩 테스트 수준에서는 기본 유형의 동적 계획법 문제가 출제되는 경우가 많음
https://mnxmnz.github.io/computer-science/dynamic-programming-in-javascript/