복잡한 문제를 간단한 여러 개의 하위 문제로 나누어 각 하위 문제의 해결책을 저장하고, 이를 활용하여 전체 문제의 해결책을 구하는 방법
중복되는 연산을 반복하지 않도록 하여, 실행 시간을 줄여 프로그램 효율성을 높이기 위해 사용되는 알고리즘이다.
중복된 하위 문제: 동적 계획법은 중복된 하위 문제의 해결책을 저장함으로써 효율적으로 문제를 해결한다.
최적 부분 구조 (Optimal Substructure): 큰 문제의 최적해가 그 문제의 하위 문제의 최적해에서 구해질 수 있다.
메모이제이션 (Memoization) 또는 테이블화 (Tabulation): 탑다운은 메모이제이션, 바텀업은 테이블화 방식을 사용하여 계산 결과를 저장한다.
1. Overlapping Subproblems(겹치는 부분 문제)
동적 계획법은 기본적으로 문제를 나누고, 그 문제의 결과값을 재활용하여 문제 전체의 답을 구하므로 동일한 작은 문제들이 반복하여 나타나는 경우에 효율적이다.
2. Optimal Substructure(최적 부분 구조)
부분 문제의 최적 결과값을 사용해 전체 문제의 최적 결과를 도출할 때 사용한다.
같은 문제는 항상 정답이 같고, 작은 문제에서 반복적으로 일어난다는 점을 활용해 큰 문제를 해결한다.
탑다운 (Top-Down) 방식 (메모이제이션)
바텀업 (Bottom-Up) 방식
1. DP로 풀 수 있는 문제인지 확인
문제 설명:
피보나치 수열에서 첫 번째와 두 번째 수는 각각 0과 1이며, 그 이후의 모든 수는 바로 앞 두 수의 합으로 이루어진다. 즉, F(0)=0, F(1)=1이고, F(n)=F(n-1)+F(n-2) (n≥2)다.
동적 계획법 접근 방법:
바텀업 방식: 0과 1로 시작하여, 배열에 이전 두 수의 합을 계속해서 저장해 나가며 n번째 피보나치 수를 구한다.
탑다운 방식 (메모이제이션): n번째 피보나치 수를 구하기 위해 재귀 함수를 사용하되, 한 번 계산한 값은 배열에 저장하여 중복 계산을 방지한다.
문제 설명:
주어진 수열에서 어떤 원소들을 삭제하고 남은 수열 중, 모든 원소가 증가하는 순서를 유지하는 수열의 최대 길이를 찾는다.
동적 계획법 접근 방법:
배열의 각 위치에 대해 그 위치를 마지막으로 하는 최장 증가 부분 수열의 길이를 저장한다.
각 위치에서 그보다 앞에 있는 원소들 중 작은 원소들을 찾고, 그 중 최대 길이에 1을 더해 현재 위치의 최대 길이를 계산한다.
문제 설명:
특정 금액을 만들기 위해 필요한 최소한의 동전 개수를 구한다. 각 동전은 무한히 많다고 가정한다.
동적 계획법 접근 방법:
금액을 만들기 위한 최소 동전 개수를 저장하는 배열을 사용한다.
각 금액에 대해 가능한 모든 동전을 시도하며, 해당 동전을 사용했을 때와 사용하지 않았을 때의 최소값을 계산해 배열을 갱신한다.
문제 설명:
한정된 무게를 가진 배낭에 최대 가치를 가지도록 물건을 담는다.
각 물건은 무게와 가치를 가지고 있으며, 물건은 쪼갤 수 없다. (0-1 배낭 문제)
각각의 무게에 대해 최대 가치를 저장하면서 전체 문제의 최적해를 구한다.
동적 계획법 접근 방법:
배열을 사용하여 특정 무게에서 담을 수 있는 물건들의 최대 가치를 저장한다.
각 물건에 대해 배낭의 무게 한도 내에서 이 물건을 추가했을 때와 추가하지 않았을 때의 최대 가치를 비교하여 배열을 갱신한다.
참고
알고리즘 - Dynamic Programming(동적 계획법)
github.com/gyoogle/tech-interview-for-developer