Loading [MathJax]/jax/output/CommonHTML/jax.js
본문 바로가기

Problem Solving/Baekjoon Online Judge (Gold)

백준 27519 - 소수의 합 (C++)

문제

  • 문제 링크
  •   
       BOJ 27519 - 소수의 합 
      
  • 문제 요약
  • 함수 f(n)을 "n0개 이상의 소수의 합으로 표현하는 경우의 수" 라고 정의하자.
    T개의 n이 주어질 때, 각 n에 맞는 f(n)의 값을 출력해보자.
  • 제한
  • TL : 3 sec, ML : 128 MB

  • 1T10,000
  • 0ni100,000

알고리즘 분류

  • 수학(math)
  • 정수론(number_theory)
  • 소수 판정(primality_test)
  • 에라토스테네스의 체(sieve)
  • 다이나믹 프로그래밍(dp)

풀이

웰노운스러운 유형 중 하나이다.

dp[n]f(n)그대로 정의할 때, 소수들을 매개로 진행하는 knapsack problem으로 바라볼 수 있다.

dp[0]=1을 기저 사례로 잡고 ni의 구간 내 존재하는 모든 소수들에 대해 냅색dp를 돌려주면 되겠다.

보통 물건의 개수가 1개임에 따라 현재 시행의 여파를 다음 시행부터 받도록 뒤에서부터 dp를 진행한다.
하지만 이번 문제에선 소수들을 중복해서 사용할 수 있으므로, 앞에서부터 진행해도 무방하다.

구간에 대해 O(nn) 수준의 소수 판정을 해도 범위 상 충분해 보이고, 맘편히 sieve를 돌려도 될 듯 하다.
나는 sieve를 진행하며 소수가 등장할 때마다 위 dp를 진행하였다.

전체 코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include<bits/stdc++.h>
#define N 100001
 
std::bitset <N> p;
int dp[N]{ 1 };
 
int main()
{
    for (int i(2); i < N; i++)
        if (!p[i])
        {
            for (int j(i + i); j < N; j += i)
                p[j] = 1;
 
            for (int j(i); j < N; j++)
            {
                dp[j] += dp[j - i];
                if (dp[j] > 1000000007)
                    dp[j] -= 1000000007;
            }
        }
    int t; scanf("%d"&t);
    while (t--)
    {
        int n; scanf("%d"&n);
        printf("%d\n", dp[n]);
    }
}
cs


comment