비슷한 문제를 몇번 풀어본 것 같은데, 감이 잘 안 잡혔고
풀이를 읽어봐도 잘 이해가 안 됐었기 때문에
논리를 한번 정리해보려한다.
1. 풀이
우선 dp로 저장할 배열의 크기는 n * k + 1이다.
dp[i][j]에 j원을 만드는 데 i번째 동전까지 고려하여 경우의 수를 저장한다. (i = 0, 1, ...)
k가 아닌 k + 1인 이유는 0원을 만드는 경우를 1로 저장하여 뒷쪽 요소에서 활용하기 위함이다.
문제에 주어진 예시를 이용해 자세히 설명해보자.
3 10
1
2
5
dp[1][5]에 5원을 만드는데 1번째 동전까지 고려하여 경우의 수를 저장할 것이다.
즉, 1원과 2원으로 5원을 만드는 경우의 수가 저장된다.
이때 1번째 동전인 2원을 기준으로 하여
안 쓰는 경우와 쓰는 경우로 나누어 생각한다.
2원을 안 쓰는 경우 - 1원으로만 5원을 만드는 경우의 수인 dp[0][5]를 가져온다.
2원을 쓰는 경우 - 몇개를 쓰는 지에 따라 나누어 생각한다.
1개를 쓴다면, (5원 - 2원 * 1개 = )3원을 1원으로만 만드는 경우의 수인 dp[0][3]을 가져온다.
2개를 쓴다면, (5원 - 2원 * 2개 = )1원을 1원으로만 만드는 경우의 수인 dp[0][1]을 가져온다.
5원까지 고려해도 이 논리가 통하는가 의문이 들 수 있으니 dp[2][10]을 설명해보자.
2번째 동전인 5원을 기준으로 하여
5원을 안쓰는 경우 - 1, 2원으로 10원을 만드는 경우의 수이므로 dp[1][10]이다.
5원을 쓰는 경우 -
5원을 1개 쓴다면, (10원 - 5원 * 1개 = )5원을 1, 2원으로만 만드는 경우의 수인 dp[1][5]을 가져온다.
2개를 쓴다면, (10원 - 5원 * 2개 = )0원을 1, 2원으로만 만드는 경우의 수인 dp[1][0]을 가져온다.
이때 0원을 만드는 경우의 수는 상식적으로 0이지만, 아까 위에서 1을 저장하겠다고 한 이유를 알 수 있다.
5원을 2개 쓰는 경우 5 5로 10원을 만드는 경우의 수 1을 얻을 수 있는데, dp[1][0]의 값을 가져오기 때문에,
이곳에 1이 저장되어 있어야한다.
즉, 0원을 만드는 경우의 수는 0이 맞지만 5 5와 같이 뒷쪽 동전만을 활용하여 최종 금액을 만드는 경우 1을 저장한 셈이 된다.
2. 메모리 초과
위와 같은 논리로 잘 푼 것 같은데
메모리 초과가 났다.
문제를 살펴보니 메모리 제한이 4MB였다.
4 MB = 4 * 10^6 B인데,
int는 4B, n <= 100, k <= 10,000 이므로
dp 배열의 최대 크기만 검색해봐도 초과한다.
4 * 100 * 10,000 = 4,000,000에서 k가 아닌 k + 1로 해야하는 점,
이외에도 다른 자잘한 변수들... 때문이다.
그래서 배열의 행을 n개가 아닌 2개로 두기로 했다.
어차피 바로 이전 행만 확인하기 때문에, 모든 행이 다 남아있을 필요가 없다.
% 연산자를 활용하여 위아래로 왔다 갔다 하며 저장했다.
자세한 구현 내용은 아래 코드 참고!!
3. 코드
'백준' 카테고리의 다른 글
[C++] 14442 벽 부수고 이동하기 2 | 그래프 탐색, 3차원 방문 체크 (0) | 2023.09.29 |
---|---|
[C++] 2512 예산 | 이진탐색(Binary Search) (0) | 2023.09.04 |
[C++] 2660 회장뽑기 | 그래프(Graph) (0) | 2023.09.03 |
여름방학 백준 스터디 후기 + 완벽주의 대한 생각 정리 (0) | 2023.09.03 |
[C++] 11000번 강의실 배정 - 문제 해결 Point (0) | 2023.08.07 |