백준 16400번: 소수 화폐

kosdjs·2025년 10월 10일

문제: https://www.acmicpc.net/problem/16400

문제 풀이

DP

dp[i]=idp[i] = i를 소수의 합으로 나타낼 수 있는 경우의 수

isPrime[i]=iisPrime[i] = i가 소수인지 여부

dp[i]=dp[iprime]dp[i] = \sum dp[i - prime] (prime(primeii보다 작은 소수))

22부터 nn까지 반복하며 소수 ii를 찾음

찾으면 ii의 배수는 소수가 아니므로 isPrimeisPrime값 변경

iiii 하나로 나타낼 수 있기 때문에 dp[i]dp[i]11 증가

그 뒤에 jji+1i + 1부터 nn까지 반복하며 jj를 소수의 합으로 나타내며 마지막에 ii를 더하는 경우의 수 dp[ji]dp[j - i]dp[j]dp[j]에 더해줌

dp[n]dp[n]을 출력하면 정답

풀이 설명

소수나라는 특이하게 모든 소수(prime number)를 화폐 단위로 사용한다. 가격이 N인 물건을 구매하려고 했다. 하지만 상점 주인이 거스름돈이 없어 정확히 N원을 지불해달라고 하였다. 소수나라의 화폐를 이용하여 N원을 정확히 만들 수 있는 방법의 가짓수를 구하여라.

즉, N을 소수의 합으로 나타낼 수 있는 가짓수를 구하는 문제다. 여기서는 더해지는 소수의 순서가 달라도 사용하는 소수의 갯수가 같으면 같은 경우의 수로 보기 때문에 순서는 상관이 없다.

따라서 소수를 작은 수부터 오름차순으로 정렬해 나타낸다고 생각하고 예시인 8을 나타내는 경우의 수를 확인하면

  1. 2 + 2 + 2 + 2
  2. 2 + 3 + 3
  3. 3 + 5

이렇게 3가지 경우의 수가 나오게 되고 여기서 마지막 수를 ii라고 한다면 8i8 - iii보다 작거나 같은 소수의 합에 ii를 더하는 형태가 되는 것을 알 수 있다. 따라서 N을 소수의 합으로 나타내는 경우의 수를 구하려면 DP를 활용해 구할 수 있다.

dp[i]dp[i]ii를 소수의 합으로 나타낼 수 있는 경우의 수로 정의한다. 또한 소수를 판별하기 위해 isPrime[i]isPrime[i]ii가 소수인지 여부로 정의한다. 22부터 NN까지 ii에 대해 반복하며 ii가 소수라면 ii의 배수는 소수가 아니기 때문에 ii의 배수 jj에 대해 isPrime[j]isPrime[j]에 false를 대입해준다.

그 이후 iiii 하나의 합으로 나타낼 수 있기 때문에 dp[i]dp[i]의 값을 1 증가 시켜준다. 그 이후에 i+1i + 1부터 NN까지의 수 jj에 대해 dp[j]dp[j]jj를 소수의 합으로 나타낸 경우의 수 중 마지막 소수가 ii인 경우의 수, 즉, jij - iii보다 작거나 같은 소수의 합으로 나타내는 경우의 수 dp[ji]dp[j - i]를 더해준다.

이를 모든 N보다 작은 소수에 대해 반복하면 dp[N]dp[N]에 N을 소수의 합으로 나타내는 경우의 수가 저장되게 된다. 이를 출력하면 정답이 된다.

소스 코드

fun main(){
    val br = System.`in`.bufferedReader()
    val n = br.readLine().toInt()
    val dp = IntArray(n + 1)
    val isPrime = BooleanArray(n + 1){true}
    for(i in 2..n){
        if(isPrime[i]){
            for(j in i * i..n step i){
                isPrime[j] = false
            }
            dp[i]++
            for(j in i + 1..n){
                dp[j] = (dp[j] + dp[j - i]) % 123456789
            }
        }
    }
    println(dp[n])
}

0개의 댓글