라떼군 이야기


6 / 16강

계산 결과를 기억하는 동적 계획법

1, 3, 4원 동전으로 6원을 만드는 최소 동전 수를 구해 봅시다. 탐욕법은 3개를 골랐지만 정답은 2개였습니다. 작은 금액의 정답을 기억하면 가능한 마지막 동전을 빠짐없이 비교할 수 있습니다.

세 가지부터 적습니다

금액이 작은 상태부터 채우면 필요한 이전 값이 이미 계산되어 있습니다. 만들 수 없는 금액은 무한대로 남겨 둡니다. 동전의 종류는 양의 정수이며 각각 무제한 사용할 수 있다고 가정합니다.

coins = [1, 3, 4]
amount = 6
dp = [float("inf")] * (amount + 1)
dp[0] = 0
for value in range(1, amount + 1):
    for coin in coins:
        if coin <= value:
            dp[value] = min(dp[value], dp[value - coin] + 1)
print(dp[amount])

출력은 2입니다. 6원의 후보는 1원 동전을 더한 dp[5]+1, 3원을 더한 dp[3]+1, 4원을 더한 dp[2]+1입니다. 그중 dp[3]+1 = 2가 가장 작습니다.

재귀와 무엇이 다를까요?

동적 계획법(DP)은 구현 문법보다 같은 상태를 다시 계산하지 않는 설계를 가리킵니다. 재귀로 필요할 때 계산해 저장하는 메모이제이션과, 작은 상태부터 표를 채우는 타뷸레이션이 있습니다. 위 코드는 후자입니다.

상태를 나눈 뒤에도 최적해를 만들 수 있어야 하고, 반복해서 만나는 상태의 답을 재사용할 수 있어야 합니다. 모든 재귀 문제가 자동으로 효율적인 DP가 되는 것은 아닙니다.

목표 금액이 A, 동전 종류가 k개면 시간은 O(Ak), 공간은 O(A)입니다. 금액이 아주 크면 이 표 자체가 부담이 됩니다.

확인 문제

동전이 2원과 4원뿐인데 3원을 만들라고 하면 어떻게 표시해야 할까요? dp[0]을 무한대로 두면 어떤 문제가 생길까요?

해설 보기

3원은 만들 수 없으므로 도달 불가능 상태로 남겨야 합니다. 사용자에게는 None 등으로 알릴 수 있습니다. dp[0]이 0이 아니면 첫 동전을 놓는 출발점이 사라져 올바른 값을 채울 수 없습니다.

제품 기획, 개발 파트너 찾으시나요? 개인, 팀, 기업 모두 환영. 문제 정의부터 출시까지 함께합니다.