1. 문제
1) 문제 링크
: https://www.acmicpc.net/problem/7512
7512번: 연속하는 소수의 합
각 테스트 케이스마다 "Scenario i:"를 출력하고, 둘째 줄에 정답을 출력한다.
www.acmicpc.net
2) 설명
숫자 n_i들이 주어지면 각각 n_i 개만큼의 연속된 소수들의 합으로 만들 수 있는 최소의 소수를 구하는 문제다.
2. 풀이
1) 알고리즘
: 에라토스테네스의 체, 슬라이딩 윈도우
2) 풀이 설명
일단 에라토스테네스의 체로 소수 배열(primes)을 구한다. 그리고 소수인지 아닌지 판별할 수 있는 isPrime배열까지채워넣는다.
사실 소수 구하는 코드는 기억을 더듬어 대강 한 거라 다른 풀이를 보는 게 더 나을 수도 있다.
이제부터 슬라이딩 윈도우로 문제를 풀면 된다.
먼저 각 케이스별로 구하는 답을 answer라고 한다.
이제 sumArray와 sumBeginIndex 배열을 이용해서 answer를 구해본다.
nArray[i] = 연속한 소수의 수
sumArray[i] = n_i개의 연속된 소수의 합으로 만든 소수
sumBeginIndex[i] = sumArray[i]의 소수를 만드는 연속된 소수들 중 첫번째 인덱스를 가리킨다.
예를 들어 nArray[i]가 3이면 sumArray와 sumBeginIndex에 가능한 값은 다음과 같다.
sumBeginIndex[3] | 0 | 1 | 2 |
sumArray[3] | 9 ( = 2 + 3 + 5 ) 근데 소수가 아니라 안 됨 | 15 ( = 3 + 5 + 7 ) 얘도 소수가 아니라 안 됨 | 23 ( = 5 + 7 + 11) 은 소수니까 ok |
sumBeginIndex[3]에는 0, 1은 들어갈 수 없고 2부터 시작하게 된다.(소수 연속합이 소수여야 하므로)
그러면 그러면 이제 n_i별로 sumArray, sumBeginIndex를 구성할 수 있다.
첫 초기값을 구하고 슬라이딩 윈도우를 통해 계속 sumArray의 모든 원소가 answer와 같아질 떄까지 슬라이딩 + answer를 업데이트 해주면 된다.
이때 sumArray[i] < answer라면, sumArray[i[ 를 슬라이딩시킨다.(그 합이 소수가 될 때까지)
만약 sumArray[i] > answer라면, 더이상 해당 answer 값은 모든 n_i 개의 연속합으로 만든 공통의 소수가 될 수 없다... i개의 연속합으로 answer를 만들 수 없다는 말이기 때문이다..
앞서 말했듯 이런 식으로 sumArray와 sumBeginIndex, answer 값을 sumArray의 모든 값과 answer 값이 같아질 때까지 반복하면 된다!
3. 코드
#include <iostream>
#include <cmath>
#include <algorithm>
#include <vector>
#include <numeric>
#define MAX_VAL 10000000
using namespace std;
vector<bool> isPrime(MAX_VAL, true); // 해당 수가 소수인지르 나타내는 배열
vector<int> primes; // 소수만 있는 배열
// 에라토스테네스의 체로 소수 구하는 함수
void setPrimes(){
isPrime[0] = isPrime[1] = false;
isPrime[2] = true;
for(int i=2;i<MAX_VAL;i++){
if(isPrime[i] == false)
continue;
primes.push_back(i);
for(int j=i*2;j<MAX_VAL;j+=i){
isPrime[j] = false;
}
}
}
// 모든 sumArray가 같은 값을 갖고 있는지 확인
bool isCorrect(vector<int> sumArray, int val){
for(auto x : sumArray)
if(x != val)
return false;
return true;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
setPrimes();
int t;
cin >> t;
for(int i=1;i<=t;i++){
int m;
cin >> m;
vector<int> nArray(m, 0); // n_i 배열
vector<int> sumArray(m, 0); // n_i만큼 연속된 소수의 합을 가진 배열(중요!! : 이 값은 소수여야 함)
vector<int> sumBeginIndex(m, 0); // sumArray에 있는 연속된 소수의 합이 어디서부터 시작했는지를 가리키는 index의 배열
//input
for(int j=0;j<m;j++)
cin >> nArray[j];
//첫 sumArray와 sumBeginIndex 구하기
for(int j=0;j<m;j++){
sumArray[j] = accumulate(&primes[0], &primes[nArray[j]], 0);
while(!isPrime[sumArray[j]]){ //구한 합이 소수인지 체크
//소수가 아니라면 슬라이딩 윈도우로 증가
sumArray[j] += primes[sumBeginIndex[j] + nArray[j]] - primes[sumBeginIndex[j]];//슬라이딩 윈도우로 다읍 값 더하고 맨 앞의 값 빼기
sumBeginIndex[j]++; //시작 인덱스 증가
}
}
int answer = *max_element(sumArray.begin(), sumArray.end()); // answer 초기값 설정, 아마 min_element로 하든 뭐로 하든 sumArray 안의 값으로 하면 상관 없을 것 같다(어차피 아래 while문 통해서 업데이트 될 거라)
while(!isCorrect(sumArray, answer)){ //sumArray 배열의 모든 값이 answer랑 똑같다면 정답을 구한 거임
for(int j=0;j<m;j++){
if(sumArray[j] < answer){
//sumArray[j] 값이 answer보다 작으면 슬라이딩 윈도우로 beg ~ end -> beg+1 ~ end+1로 합 증가
//이걸 새로 구한 소수의 연속합이 소수가 될 때까지 반복
//초기값 구할 때와 달리 do while문인 이유는 얘는 이미 소수의 연속합이 answer보다 작기 때문에 최소한 한 번은 무조건 업데이트를 해줘야 하기 때문이다. 주석처리된 코드처럼 sumArray[j] += ~~~~~~; while(!isPrime[sumArray[j]]){~~~}로 해도 되지만 슬라이딩 윈도우하는 코드가 중복돼서 그냥 do while문으로 해줬다
/*
sumArray[j] += primes[sumBeginIndex[j] + nArray[j]] - primes[sumBeginIndex[j]];
sumBeginIndex[j]++;
while(!isPrime[sumArray[j]]){
sumArray[j] += primes[sumBeginIndex[j] + nArray[j]] - primes[sumBeginIndex[j]];
sumBeginIndex[j]++;
}
*/
do {
sumArray[j] += primes[sumBeginIndex[j] + nArray[j]] - primes[sumBeginIndex[j]];
sumBeginIndex[j]++;
} while(!isPrime[sumArray[j]]);
} else {
//sumArray 중에 answer보다 큰 값이 있으면 answer 값 업데이트
answer = sumArray[j];
}
}
}
cout << "Scenario " << i << ":\n";
cout << answer << "\n\n";
}
return 0;
}
4. 느낀점
개인적으로 소수 문제는 별로 선호하지 않는다.. (어렵기 때문 ㅎㅎ)
그래도 머리 싸매고 풀어내니까 기분이 좋더라..
근데 정작 내가 푼 알고리즘을 설명을 못 하겠다..
너무 복잡하게 푼 게 아닐까 싶어서 맞은 분들의 다른 풀이를 봤는데... 잘 모르겠더라..ㅎㅎㅎ
많이 부족함을 느낀다..
'알고리즘 > 백준' 카테고리의 다른 글
[백준/2281] 데스노트(1차원 dp) (2) | 2021.10.01 |
---|---|
[백준/1727] 커플 맞추기 (0) | 2021.08.25 |
[백준/2110] 공유기 설치 (0) | 2021.08.01 |
[BOJ/1034] 램 (0) | 2021.07.27 |
[BOJ/7579] 앱 (0) | 2020.12.29 |