체크 포인트
1. 다이나믹 프로그래밍(Dynamic Programming, 동적계획법)을 이용
-큰 문제를 여러 가지 작은 문제로 나누어 해결해보는 DP를 이용했다.
-예시 1의 예를 들면, 3종류의 동전(1원, 2원, 5원)으로 15원을 만드는 최소의 동전개수를 구해야 한다.
-동적 계획법에 따라, 작은 문제로부터 큰 문제에 대한 해결을 추론해 보았다.
1) 1원에서부터 15원까지
(1)1원,
(2)1원+2원,
(3)1원+2원+5원
세 가지 경우로 나누고, 구성할 수 있는 동전의 최소 개수를 구하며 규칙성을 찾아보았다. 해당 규칙성을 찾기 위해 M=0일 때는 동전의 종류에 상관없이 0을 입력했다.
(1)1원을 이용한 동전의 최소 개수
M | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
1원 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
-1원으로 구성할 수 있는 동전의 최소 개수는 M과 동일하다.
(2)1원과 2원을 이용한 동전의 최소 개수
M | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
1원 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
1원 + 2원 |
0 | 1 | 1 0+1 |
2 1+1 |
2 1+1 |
3 2+1 |
3 2+1 |
4 3+1 |
4 3+1 |
5 4+1 |
5 4+1 |
6 5+1 |
6 5+1 |
7 6+1 |
7 6+1 |
8 7+1 |
-M이 1일 때는, 2원짜리 동전을 사용할 수 없기 때문에 그대로 1로 유지한다.
-M이 2 이상일 때부터 2원을 사용할 수 있기 때문에 비교를 하며 최소 개수를 구해야 한다.
-M=2일 때, 동전 합이 M=0일 때 최소 개수(0)에 2원을 더하면(1) 최소 개수 1개가 된다.
-M=3일 때, 1원까지의 최소 개수(1)에 2원을 더하면(1) 최소 개수 2개가 된다.
-M=4일 때, 2원까지의 최소 개수(1)에 2원을 더하면(1) 최소 개수 2개가 된다.
...
-M=15일 때, 13원까지의 최소 개수(7)에 2원을 더하면(1) 최소 개수 8개가 된다.
dp[i] = dp[i-coins[j]] + 1
예시로부터 위의 식을 추론할 수 있다.
하지만 위의 식은 항상 성립하는 것이 아니다.
만약 동전이 1원, 6원, 9원 세 종류가 있고, 12원의 거스름돈을 만드는 상황이라고 가정하자. 실제 최소의 개수는 2개(6+6)이지만, 위의 식대로만 진행한 경우 최소의 개수는 4개 (1+1+1+9)가 되어버린다.
dp[i] = Math.min(dp[i], dp[i-coins[j]] + 1);
따라서 위의 식에 최소 개수를 비교해주는 조건을 추가해야 한다. 이를 토대로 5원까지 추가한 경우 아래와 같은 식을 구할 수 있다.
(3)1원과 2원과 5원을 이용한 동전의 최소 개수
M | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
1원 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
1원 + 2원 |
0 | 1 | 1 | 2 | 2 | 3 | 3 | 4 | 4 | 5 | 5 | 6 | 6 | 7 | 7 | 8 |
1원 + 2원 + 5원 |
0 | 1 | 1 | 2 | 2 | 1 | 2 | 2 | 3 | 3 | 2 | 3 | 3 | 4 | 4 | 3 |
풀이
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
// 동전의 종류를 담는 배열
static int[] coins;
// 동전합을 이루는 최소의 개수를 담는 배열
static int[] dp;
// 동전 종류의 개수, 동전의 최소 개수 변수
static int N,count;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
// 동전의 종류 변수
N = Integer.parseInt(st.nextToken());
// 동전의 종류를 담아줄 배열 선언
coins = new int[N];
st = new StringTokenizer(br.readLine(), " ");
// 동전 종류 담기
for(int i=0; i<N; i++){
coins[i] = Integer.parseInt(st.nextToken());
}
// 거슬러 줄 금액
int M = Integer.parseInt(br.readLine());
// 동전의 최소 개수를 담는 배열 초기화
dp = new int[M+1];
// 0번째를 0으로 초기화
dp[0] = 0;
// 탐색 시작
solution(M);
} // end of Main
static void solution(int M) {
for(int i=1; i<=M; i++){
// 배열의 각 자리에 들어갈 동전의 최소개수를 초기화
dp[i] = Integer.MAX_VALUE;
}
// 각 종류의 동전 별 탐색,
for(int j=0; j<N; j++){
// 0번째 동전부터, 거슬러 줄 금액을 만들 수 있는 동전의 최소 개수 탐색
for(int i=coins[j]; i<=M; i++){
// 거슬러줄 동전의 최소 개수 dp[i]는
// 기존의 dp[i]와,
// coins[j] 동전을 사용한 dp[i-coins[j]]+1 중에 최소값으로 한다.
dp[i] = Math.min(dp[i], dp[i-coins[j]] + 1);
}
}
System.out.println(dp[M]);
} // end of solution
} // end of Main
'코딩테스트' 카테고리의 다른 글
[인프런 자바/java] 10. 가장 짧은 문자거리 _디버깅의 눈물 (1) | 2022.09.29 |
---|---|
[백준-1912번 자바/java] 연속합 _디버깅의 눈물 (1) | 2022.09.23 |
[백준-2293번 자바/java] 동전 1 _디버깅의 눈물 (1) | 2022.09.22 |
[백준-2579번 자바/java] 계단 오르기 _디버깅의 눈물 (1) | 2022.09.21 |
[백준-11726번 자바/java] 2×n 타일링 _디버깅의 눈물 (0) | 2022.09.21 |