성장, 그 아름다운 향연

https://www.acmicpc.net/problem/2294

 

2294번: 동전 2

첫째 줄에 n, k가 주어진다. (1 ≤ n ≤ 100, 1 ≤ k ≤ 10,000) 다음 n개의 줄에는 각각의 동전의 가치가 주어진다. 동전의 가치는 100,000보다 작거나 같은 자연수이다. 가치가 같은 동전이 여러 번 주

www.acmicpc.net

 

문제

n가지 종류의 동전이 있다. 이 동전들을 적당히 사용해서, 그 가치의 합이 k원이 되도록 하고 싶다. 그러면서 동전의 개수가 최소가 되도록 하려고 한다. 각각의 동전은 몇 개라도 사용할 수 있다.

사용한 동전의 구성이 같은데, 순서만 다른 것은 같은 경우이다.

입력

첫째 줄에 n, k가 주어진다. (1 ≤ n ≤ 100, 1 ≤ k ≤ 10,000) 다음 n개의 줄에는 각각의 동전의 가치가 주어진다. 동전의 가치는 100,000보다 작거나 같은 자연수이다. 가치가 같은 동전이 여러 번 주어질 수도 있다.

출력

첫째 줄에 사용한 동전의 최소 개수를 출력한다. 불가능한 경우에는 -1을 출력한다.

 

 


 

접근 방법

 

동전 1 문제에 다음과 같은 조건이 추가된 문제다.

1. 동전의 개수가 최소가 되도록

2. 사용한 동전의 최소 개수를 출력하기

 

 

우선 dp 관점에서 동전 2 문제를 푸는 방법을 살펴보자.

1) D[0]일 때는 값이 얼마일까?

합이 0이어야 하고, 최소 개수를 출력해야 하기 때문에 경우의 수는 0이다.

 

참고) 만약 최소 조건이 없다면?

D[0] = 1이어야 한다. 모든 경우의 수를 고려해야 하기 때문에 k = 1 이라면 D[1]에는 D[0]을 더한 값(0+1)이 저장되어야 한다.

그래야 올바르게 D[1]부터 저장될 수 있다.

 

2) 조건 1 : 최소가 되도록 한다.

우리가 찾고자 하는 동전의 합 k에 대해서, 1, 5, 12 의 동전이 있다고 가정한다.

 

k = 1이 될 때는,

1 -> 1개이다.

 

k = 2가 될 때는,

1 + 1 -> 1개이다.

 

...

 

k = 5가 될 때는,

1 + 1 + 1 + 1 + 1

5

-> 2개이다.

 

하지만 우리는 최소를 구해야 하기 때문에 1 + 1 + 1 + 1 + 1 을 제외해야 k=5 가 될 때 최소가 되는 것은 5 하나이므로

-> 1개이다.

 

3) 사용한 동전 구성은 같으나 순서만 다른 것은 같은 경우로 따져야 한다.

이를 구현하기 위해서는 1-K까지의 범위를 구하면서 동전들을 추가하지 말고, 동전들을 기준으로 1-K까지 더해준다.

 

1) 1-K 까지 범위를 구하면서 동전을 추가

 

1 = 1

2 = 1 + 1

3 = 1 + 1 + 1

...

5 = 1 + 1 + 1 + 1 + 1, 5

6 = 1 + 1 + 1 + 1 + 1 + 1, 1 + 5, 5 + 1

 

-> 순서가 뒤바뀔 수 있다.

 

 

2) 동전들을 기준으로 1-K까지 추가

 

동전 = 1일 때

1 = 1

2 = 1 + 1

3 = 1 + 1 + 1

...

5 = 1 + 1 + 1 + 1 + 1

6 = 1 + 1 + 1 + 1 + 1 + 1

 

동전=5일 때

1 = 1

2 = 1 + 1

3 = 1 + 1 + 1

...

5 = 1 + 1 + 1 + 1 + 1, 5

6 = 1 + 1 + 1 + 1 + 1 + 1, 1 + 5

 

->  동전의 순서는 크게 상관없지만, 1,5 순서라고 하면 1을 다 채운 후에 5를 채우게 되므로 순서를 항상 보장한다!

 

4) 점화식을 세워보자.

D[k] = 합이 k일 때의 최소의 동전 개수

D[k] = min(D[k], D[j-k] + 1) (j는 1-k까지일 때의 동전의 크기)

 

5) 마지막으로 동전의 개수를 셀 수 없는 경우를 고려하기

for(i in a) {
        for(j in 1..k) {
            if(j-i >= 0 && dp[j-i] != -1) {
                if(dp[j]== -1 || dp[j] > dp[j-i] + 1)
                    dp[j] = dp[j-i] + 1
            }
        }
    }

 

 

처음에 dp 배열 안에 -1 을 모두 저장시켰다.

dp[j-i] != -1 을 해준 이유는 만약 dp[j-i]에 아무것도 저장되지 않아 초기값 -1이 저장된 상태에서 경우의 수를 더해주는 것을 방지하는 역할을 나타내기 위함이다.

dp[j]== -1 || dp[j] > dp[j-i] + 1 조건은 최솟값을 찾게 한다. 

-1이 저장돼있으면 아직 최소 비교를 하지 않았으므로 그대로 넣고,

그것이 아니라면 최소 비교를 해준다.

 

 

소스 코드

 

fun main() = with(System.`in`.bufferedReader()) {
    val (n,k) = readLine().split(" ").map { it.toInt() }
    val a = IntArray(n)
    val dp = IntArray(10001) {-1}

    repeat(n) {
        a[it] = readLine().toInt()
    }

    dp[0] = 0
    for(i in a) {
        for(j in 1..k) {
            if(j-i >= 0 && dp[j-i] != -1) {
                if(dp[j]== -1 || dp[j] > dp[j-i] + 1)
                    dp[j] = dp[j-i] + 1
            }
        }
    }
    println(dp[k])
}

 

'algorithm > 다이나믹 프로그래밍' 카테고리의 다른 글

백준 5557) 1학년  (0) 2021.10.14
백준 9252) LCS 2  (0) 2021.10.13
백준 10942) 팰린드롬?  (0) 2021.10.03
백준 9251) LCS  (0) 2021.05.06
백준 11053) 가장 긴 증가하는 부분 수열(LIS)  (0) 2021.05.01
profile

성장, 그 아름다운 향연

@dev_minoflower

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

profile on loading

Loading...