효율적인 화폐 구성
문제
문제 정의
- N가지 종류의 화폐가 있다. 이 화폐들의 개수를 최소한으로 이용해서 그 가치의 합이 M원이 되도록 하려고 한다.
- 이때 각 화폐는 몇 개라도 사용할 수 있으며, 사용한 화폐의 구성은 같지만 순서만 다른 것은 같은 경우로 구분한다.
- 예시
- 2원, 3원 단위의 화폐가 있을 때는 15원을 만들기 위해 3원을 5개 사용하는 것이 가장 최소한의 화폐 개수이다.
입력조건
- 첫째 줄에 N, M이 주어진다.
- N: 1 이상, 100 이하
- M: 1 이상, 10,000 이하
- 이후 N개의 줄에는 각 화폐의 가치가 주어진다.
화폐 가치는 10,000보다 작거나 같은 자연수이다.
출력조건
- 첫째 줄에 M원을 만들기 위한 최소한의 화폐 개수를 출력한다.
- 불가능할 때는 -1을 출력한다.
입·출력 예시 - 1
- 입력
2 15 2 3
- 출력
5
입·출력 예시 - 2
- 입력
3 4 3 5 7
- 출력
-1
풀이
문제 해설 - 내 풀이
- 현재 금액 i를 만드는 방법은 아래와 같다.
(i-화폐단위)+1
을 수행하면 현재 금액 i를 만드는 화폐개수를 구할 수 있다.- 단 화폐단위가 여러개이므로, 현재 금액 i를 만드는 화폐 개수 중 가장 작은 것을 선택해야 한다.
- 점화식
- : 금액 i를 만드는 최소한의 화폐 개수
- : 전체 화폐단위 중, t번째 화폐단위 (t는 까지 존재한다.)
문제 해설 - 교재 풀이
- 적은 금액부터 큰 금액까지 확인하면서 차례대로 ‘만들 수 있는 최소한의 화폐 개수’ 를 찾으면 된다.
- 점화식
- : 금액 i를 만드는 최소한의 화폐 개수
- : 화폐 단위
- 단, 의 초기값은 무한대이다.
소스코드
public class 효율적인_화폐_구성 {
static int[] d = new int[10000];
/** 점화식 (내 풀이)
d[i] = min(d[i-n[0]], d[i-n[1]], ..., d[i-n[n.length-1]])
*/
private static int solution1(int[] n, int m) {
//d[i] = 금액 i를 표현할 수 있는 최소 화폐 수
//배열 d 초기화
Arrays.fill(d, -1);
d[0] = 0;
for (int nowCost = 0; nowCost <= m; nowCost++) {
int tmp = Integer.MAX_VALUE;
for (int moneyUnit : n) {
// if (nowCost - moneyUnit >= 0 && d[nowCost-moneyUnit] != -1 && tmp > d[nowCost-moneyUnit]+1) {
// tmp = d[nowCost-moneyUnit]+1;
// d[nowCost] = tmp;
// }
// 아래와 동일한 로직
if (nowCost - moneyUnit >= 0) { //존재 가능한 index이고
if (d[nowCost - moneyUnit] != - 1) { //화폐단위로 표현할 수 있었던 것이고
if (tmp > d[nowCost - moneyUnit] + 1) { //그것이 최소값이라면
tmp = d[nowCost-moneyUnit]+1;
d[nowCost] = tmp; //금액 i를 표현할 수 있는 최소 화폐 수를 저장한다.
}
}
}
} //내부 for문 종료
} //외부 for문 종료
return d[m];
}
/** 점화식 (교재풀이)
금액 i를 만들 수 있다면: d[i] = min(d[i], d[i-k]+1), 단 k는 화폐 단위
금액 i를 만들 수 없다면: d[i] = 10001
*/
private static int solution2(int[] n, int m) {
//d[i] = 금액 i를 만드는데 필요한 최소 화폐 수
Arrays.fill(d, 10001); //m의 최대값이 10000이므로, 필요한 화폐 개수가 10000을 넘어간다면 만들 수 없는 금액이라는 뜻이다.
d[0] = 0;
//각 화폐단위를 하나씩 적용하며, 해당 화폐단위로 만들 수 있는 금액의 최소 화폐 수를 갱신한다.
for (int moneyUnit : n) {
for (int nowMoney = moneyUnit; nowMoney <= m; nowMoney++) {
if (d[nowMoney - moneyUnit] != 10001) {
d[nowMoney] = Math.min(d[nowMoney], d[nowMoney - moneyUnit] + 1);
}
}
}
if (d[m] == 10001) {
return -1;
}
return d[m];
}
public static void execute() throws Exception {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
String s = bf.readLine();
StringTokenizer st = new StringTokenizer(s);
int sizeOfN = Integer.parseInt(st.nextToken());
int m = Integer.parseInt(st.nextToken());
int[] n = new int[sizeOfN];
for (int i = 0; i < sizeOfN; i++) {
n[i] = Integer.parseInt(bf.readLine());
}
TimeCheck.start();
int answer = solution2(n, m);
TimeCheck.end();
System.out.println(answer);
}
}
교재 풀이가 내 풀이보다 깔끔하다.
- 나동빈, 『이것이 코딩 테스트다』