
난이도 : 골드 4
유형 : DP 심화
https://www.acmicpc.net/problem/2293
n가지 종류의 동전이 있다. 각각의 동전이 나타내는 가치는 다르다. 이 동전을 적당히 사용**해서, 그 가치의 합이 k원이 되도록 하고 싶다. 그 경우의 수를 구하시오. 각각의 동전은 몇 개라도 사용할 수 있다.
사용한 동전의 구성이 같은데, 순서만 다른 것은 같은 경우이다.
첫째 줄에 n, k (1<=n<=100, 1<=k<=10,000)
첫째 줄에 경우의 수 출력, 경우의 수는 2^31 보다 작다.
어떠한 가치를 지닌 동전을 사용하여 목표 가치 k를 표현할 수 있는 경우의 수를 구하는 문제이다.
예제 1은 1, 2, 5 의 가치를 지닌 동전을 통해 목표 가치 10에 도달할 수 있는 경우의 수는
1,1,1,1,1,1,1,1,1,1
1,1,1,1,1,1,1,1,2
1,1,1,1,1,1,2,2
1,1,1,1,2,2,2
1,1,2,2,2,2
2,2,2,2,2
1,1,1,1,1,5
1,1,1,2,5
1,2,2,5
5,5
이렇게 10가지이다.
어떻게 풀 수 있을까
이 문제는 동전의 조합을 통해 목표 금액을 만드는 경우의 수를 구하는 전형적인 DP(다이나믹 프로그래밍) 문제다.
특히, 동전의 순서는 고려하지 않으며, 각 동전은 무한히 사용할 수 있다는 점에서 중복 조합(Unbounded Knapsack) 유형으로 접근할 수 있다.
dp[value] += dp[value - coin]인데 어떻게 나온거냐면dp[value] += dp[value - coin]import sys
input = sys.stdin.readline
n, target = map(int,input().split())
coins = [int(input()) for _ in range(n)]
dp = [0] * (target + 1) # 0부터 target까지 만드는 방법의 경우의 수
dp[0] = 1 # 0원을 만드는 방법은 '아무것도 선택하지 않는 방법' 1가지
for coin in coins: # 동전 리스트를 바깥쪽에 두어 중복을 세지않게 처리함
for value in range(coin, target+1):
dp[value] += dp[value-coin]
print(dp[target])
n = 3, target = 10
coins = [1, 2, 5] 일때
for coin in coins:
for value in range(1, target + 1):
dp[value] += dp[value - 1]
는
for coin in coins:
for value in range(1, 11):
dp[value] += dp[value-1] 이 되어
코인이 1일때,
| value | 계산 | dp 결과 |
|---|---|---|
| 1 | dp[1] += dp[0] → 1 | [1, 1, 0, ...] |
| 2 | dp[2] += dp[1] → 1 | [1, 1, 1, ...] |
| ... | ... | ... |
| 10 | dp[10] += dp[9] → 1 | [1, 1, 1, ..., 1] |
dp = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
이 되고
코인이 2일때,
'for value in range(2, 11):
dp[value] += dp[value - 2]
| value | 계산 | dp 결과 |
|---|---|---|
| 2 | dp[2] += dp[0] → 2 | [1, 1, 2, ...] |
| 3 | dp[3] += dp[1] → 2 | [1, 1, 2, 2, ...] |
| 4 | dp[4] += dp[2] → 3 | [1, 1, 2, 2, 3, ...] |
| ... | ... | ... |
| 10 | dp[10] += dp[8] → 6 | [1, 1, 2, ..., 10] |
dp = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6]
가 되며
마지막 코인이 5일때,
for value in range(5, 11):
dp[value] += dp[value - 5]
| value | 계산 | dp 결과 |
|---|---|---|
| 5 | dp[5] += dp[0] → 4 | [1, 1, 2, 2, 3, 4, ...] |
| 6 | dp[6] += dp[1] → 5 | [1, 1, 2, 2, 3, 4, 5, ...] |
| 7 | dp[7] += dp[2] → 6 | ... |
| ... | ... | ... |
| 10 | dp[10] += dp[5] → 10 | [1, 1, 2, 2, 3, 4, ..., 10] |
최종적으로 dp열은 dp = [1, 1, 2, 2, 3, 4, 5, 6, 7, 8, 10]
가 된다.
dp[target]인 dp[10] = 10
특정 금액을 만들 수 있는 '경우의 수'를 구할 땐, 항상 dp를 배열로 두고, 작은 단위부터 누적해가는 방식을 떠올리자.
순열이 아니라 조합이라는 조건이 중요하며, 이 때문에 동전 루프를 바깥에 두는 순서가 핵심이 된다.