Link
https://www.acmicpc.net/problem/1415
Detail
순서를 고려하지 않고 사탕을 담았을 때 소수인 경우의 수를 구해야 하므로, 이는 배낭문제 로써 해석할 수 있다.
또한 소수임을 판별해야 하기 때문에, 에라토스테네스의 채를 사용하여 미리 소수처리를 해주면 해결이 가능하다.
Code
#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
bool prime[500001];
void buildPrime(){
memset(prime, true, sizeof(prime));
prime[0] = prime[1] = false;
for(int i = 2; i <= 500000; i++){
if(prime[i]){
for(int j = i * 2; j <= 500000; j += i)
prime[j] = false;
}
}
}
// 소수 판별 함수 (2부터 sqrt(n)까지 확인)
bool isPrime(int n) {
return prime[n];
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
buildPrime();
int N;
cin >> N;
// 가격별 빈도를 저장
map<int, int> freq;
int price;
int total_sum = 0;
for(int i = 0; i < N; i++){
cin >> price;
freq[price]++;
total_sum += price;
}
// dp[s]: 현재까지 선택한 사탕들의 가격 합이 s가 되는 경우의 수
// 모든 조합은 모양(가격별 선택 개수)로 구분하므로, 순서와 무관하게 한 번씩만 센다.
vector<ll> dp(total_sum + 1, 0);
dp[0] = 1;
// 각 그룹에 대해 DP 전개: 같은 가격의 사탕이 count개 있다면 0개부터 count개까지 선택할 수 있다.
for(auto &p : freq){
int p_val = p.first;
int count = p.second;
vector<ll> newdp(total_sum + 1, 0);
for(int s = 0; s <= total_sum; s++){
if(dp[s] != 0){
// 이 그룹에서 k개를 선택하면 총합이 s + k*p_val 가 된다.
for (int k = 0; k <= count; k++){
if(s + k * p_val <= total_sum)
newdp[s + k * p_val] += dp[s];
}
}
}
dp = newdp;
}
// 빈 구매(아무것도 선택하지 않는 경우)는 소수가 될 수 없으므로 s=2부터 확인
long long ans = 0;
for (int s = 2; s <= total_sum; s++){
if(isPrime(s))
ans += dp[s];
}
cout << ans << "\n";
return 0;
}