다이나믹 프로그래밍: 메모리를 적절히 사용하여 수행 시간 효율성을 비약적으로 향상시키는 방법
이미 계산된 결과(작은 문제)는 별도의 메모리 영역에 저장하여 다시 계산하지 않음
일반적으로 두 가지 방식(Top-down, Bottom-up)으로 구현됨
동적 계획법이라고도 부름
다이나믹 프로그래밍의 조건
최적 부분 구조 (Optimal Substructure)
중복되는 부분 문제 (Overlapping Subproblem)
피보나치 수열 문제를 다이나믹 프로그래밍을 통해 효율적으로 해결할 수 있음
메모이제이션(Memoization)
Top-down vs Bottom-up
다이나믹 프로그래밍 vs 분할 정복
다이나믹 프로그래밍 문제에 접근하는 방법
문제 해결 아이디어
‘ai = i번째 식량창고까지의 최적의 해 (얻을 수 있는 식량의 최댓값)’ 으로 정의한다면 다이나믹 프로그래밍을 적용할 수 있음
왼쪽부터 차례대로 식량창고를 턴다고 했을 때, 특정한 i번째 식량창고에 대해서 털지 안 털지의 여부를 결정하면, 2가지 경우 중 더 많은 식량을 털 수 있는 경우를 선택
ai = i번째 식량창고까지의 최적의 해 (얻을 수 있는 식량의 최댓값)
ki = i번째 식량창고에 있는 식량의 양
점화식: ai = max(ai-1, ai-2 + ki)
한 칸 이상 떨어진 식량창고는 항상 털 수 있으므로 i-3번째 이하는 고려할 필요가 없음
코드
# 답안 예시
# 정수 N을 입력 받기
n = int(input())
# 모든 식량 정보 입력 받기
array = list(map(int, input().split())
# 앞서 계산된 결과를 저장하기 위한 DP 테이블 초기화
d = [0] * 100
# 다이나믹 프로그래밍 진행 (Bottom-up)
d[0] = array[0]
d[1] = max(array[0], array[1])
for i in range(2,n):
d[i] = max(d[i-1], d[i-2] + array[i])
# 계산된 결과 출력
print(d[n-1])
문제 해결 아이디어
코드
# 답안 예시
# 정수 X를 입력 받기
x = int(input())
# 앞서 계산된 결과를 저장하기 위한 DP 테이블 초기화
d = [0] * 30001
# 다이나믹 프로그래밍 진행 (Bottom-up)
for i in range(2, x+1):
# 현재의 수에서 1을 빼는 경우
d[i] = d[i-1] + 1
# 현재의 수가 2로 나누어 떨어지는 경우
if i % 2 == 0:
d[i] = min(d[i], d[i//2] + 1)
# 현재의 수가 3으로 나누어 떨어지는 경우
if i % 3 == 0:
d[i] = min(d[i], d[i//3] + 1)
# 현재의 수가 5로 나누어 떨어지는 경우
if i % 5 == 0:
d[i] = min(d[i], d[i//5] +1)
print(d[x])
N = 3, M = 7 이고, 각 화폐의 단위가 2, 3, 5인 경우
코드
# 정수 N,M을 입력 받기
n, m = map(int, input().split())
# N개의 화폐 단위 정보를 입력받기
array = []
for i in range(n):
array.append(int(input())
# 한 번 계산된 결과를 저장하기 위한 DP 테이블 초기화
d = [10001] * (m + 1)
# 다이나믹 프로그래밍 진행 (바텀업)
d[0] = 0
for i in range(n):
for j in range(array[i], m+1): # j는 금액을 의미
if d[j - array[i]] != 10001: # (i-k) 원을 만드는 방법이 존재하는 경우
d[j] = min(d[j], d[j-array[i]] + 1)
# 계산된 결과 출력
if d[m] = 10001: # 최종적으로 m원을 만드는 방법이 없는 경우
print(-1)
else:
print(d[m])
# Test Case 입력
for tc in range(int(input())):
# 금광 정보 입력
n, m = map(int, input().split())
array = list(map(int, input().split()))
# 다이나믹 프로그래밍을 위한 2차원 DP 테이블 초기화
dp = []
index = 0
for i in range(n):
dp.append(array[index:index+m])
index += m
# 다이나믹 프로그래밍 진행
for j in range(1,m): # 열 기준 확인
for i in range(n):
# 왼쪽 위에서 오는 경우
if i == 0: left_up = 0 # 인덱스를 벗어나면 해당 경로의 값을 0 으로 초기화
else: left_up = dp[i-1][j-1]
# 왼쪽 아래에서 오는 경우
if i == n - 1: left down = 0 # 인덱스를 벗어나면 해당 경로의 값을 0 으로 초기화
else: left down = dp[i+1][j-1]
# 왼쪽에서 오는 경우
left = dp[i][j-1]
dp[i][j] = dp[i][j] + max(left_up, leftdown, left)
result = 0
for i in range(n):
result = max(result, dp[i][m-1]
print(result)
문제 해결 아이디어
코드
n = int(input())
array = list(map(int, input().split()))
# 순서를 뒤집어 'LIS' 문제로 변환
array.reverse()
# 다이나믹 프로그래밍을 위한 1차원 DP 테이블 초기화
dp = [1] * n
# LIS 알고리즘 수행
for i in range(1, n):
for j in range(0, i):
if array[j] < array[i]:
dp[i] = max(dp[i], dp[j] + 1)
# 열외해야 하는 병사의 최소 수를 출력
print(n-max(dp))
참고: 이것이 취업을 위한 코딩 테스트다 with 파이썬 (취업과 이직을 결정하는 알고리즘 인터뷰 완벽 가이드), 유튜브 강의 영상