Finn은 편의점에서 야간 아르바이트를 하고 있습니다. 야간에 손님이 너무 없어 심심한 Finn은 손님들께 거스름돈을 n 원을 줄 때 방법의 경우의 수를 구하기로 하였습니다.
예를 들어서 손님께 5원을 거슬러 줘야 하고 1원, 2원, 5원이 있다면 다음과 같이 4가지 방법으로 5원을 거슬러 줄 수 있습니다.
거슬러 줘야 하는 금액 n과 Finn이 현재 보유하고 있는 돈의 종류 money가 매개변수로 주어질 때, Finn이 n 원을 거슬러 줄 방법의 수를 return 하도록 solution 함수를 완성해 주세요.
n | money | result |
---|---|---|
5 | [1,2,5] | 4 |
문제를 보자마자, 전체 경우를 따지는 재귀함수로 풀어내는 방법이 생각났다.
(하지만, MOD 연산이 있어서, 재귀함수가 아닐 수 도 있다는 생각이 곧바로 따라왔다.)
그래도 일단, 재귀함수로 풀어보았다.
전체 경우를 따지는 방법은 불량 사용자 문제와 마찬가지로 풀어낼 수 있다.
function solution(n, money) {
const MOD = 1000000007;
const allCases = new Set;
const len = money.length;
function Recursive(left, cases) {
if (left === 0) {
allCases.add(cases.sort().join(''));
return;
}
for (let i = 0; i < len; i++) {
if (money[i] <= left) {
Recursive(left-money[i], [...cases, money[i]])
}
}
return;
}
Recursive(n, []);
return allCases.size % MOD
}
사실상 MOD 연산을 하지 않아도 채점 결과는 동일했다.
따라서, 풀이는 맞지만, 출제 의도와는 다른 풀이라고 생각한다.
DP풀이를 생각해보려면 주어진 경우를 분석할 필요가 있다.
, , , , , , ,
1, 2, 5 로 구성된 경우 더 구해보면
일 것 같지만, 계산결과는 다르다.
우변의 세개에 중복되는 경우가 생기게 된다.
따라서, 단순히 이 커감에 따라 money
의 모든 요소를 빼서 더하면 안된다.
그래서, money
의 요소를 기준으로 순회하면서 경우를 따져보자.
우선 동전 1
로 n
까지 만들 수 있는 경우는 모두 한가지이다.
1 :
2 :
3 :
4 :
5 :
그 이후, 동전 2
를 사용해서 n
까지 만들 수 있는 경우를 따지면,
1 :
2 :
3 :
4 :
5 :
위와 같이 된다. ( 이후가 추가된 것)
이렇게 동전을 기준으로 반복하며 DP를 채워나가면 된다.
따라서, 풀이는 다음과 같다.
function solution (n, money){
const MOD = 1000000007;
const dp = new Array(n + 1).fill(0);
dp[0] = 1;
for(let elem of money){
for(let i = elem; i < n+1; i++){
dp[i] = (dp[i] + dp[i - elem]) % MOD;
}
}
return dp[n];
}
당연히 순차적으로 DP 배열을 채워나갈 생각으로 접근하면 안풀리는 DP문제이다...