소수들의 연속합을 구하는 문제이다. 어떤 수를 입력으로 받아, 그 수를 소수의 연속합으로 어떻게 표현할 수 있는지 개수를 출력해주면 된다. 문제 제목이 너무 문제 내용이다...
일단 소수의 리스트를 가지고 있어야 하므로, 에라토스테네스의 체를 사용해 N의 최대범위 안의 모든 소수를 구한다.
소수의 리스트를 완성한 후, 투 포인터 알고리즘을 사용해 연속합을 구해가며 자연수 N을 찾는다.
소수가 들어있는 배열이 있고, 그 배열의 원소를 가르키는 Right(R) 인덱스와, Left(L) 인덱스를 만든다. 두 인덱스는 첫번째 원소를 가르킨다. 여기에, 현재 합을 저장하는 sum 변수를 만들어 주면 연산할 준비가 되었다.
만약, sum이 우리가 찾는 N 보다 작으면 R을 한칸 증가시킨다.
R을 증가시키고, R이 가르키는 원소를 sum에 더한다. 이 과정을 sum이 N과 같거나 클때까지 반복한다.
만약 N = 41 이라 가정하면, 2 + 3 + 5 + 7 + 11 = 41이다. 우리가 찾는 조합이 완성되었다. 이제 조합의 개수를 한개 증가시키고 다음으로 넘어가야 한다. 이 상황에서 R을 한칸 증가시키는 것은 의미가 없다. 무조건 N보다 커지므로 우리는 이제 L을 한칸 증가시킨다.
그러면 sum = 39가 되고, 다시 N보다 작아지게 된다. 여기서 R을 또 증가시킨다.
3 + 5 + 7 + 11 + 13 = 41이다. 또 조합의 수를 늘려주고, L을 증가시켜 같은 연산을 반복한다. 이 반복의 종료조건은, L이 N 보다 커질때다. L이 N 과 같을때는 원소 개수를 한개로 볼 수 있지만, 그것보다 커지면 아무 의미없는 연산이 되므로 해당 조건으로 루프를 돌리게 된다.
#include <iostream>
#include <string.h>
bool primeFilter[4000001];
int main()
{
memset(primeFilter, 1, sizeof(primeFilter));
primeFilter[0] = 0;
primeFilter[1] = 0;
for (int i = 2; i <= 4000000 ; i++)
{
if (primeFilter[i]){
for (int j = 2*i; j <= 4000000; j+=i)
primeFilter[j] = false;
}
}
int lPointer = 2;
int rPointer = 2;
int sum = 2;
int N;
int found = 0;
std::cin >> N;
while (lPointer <= N){
if (sum < N)
{
while(!primeFilter[++rPointer]);
sum += rPointer;
}
else if (sum > N)
{
sum -= lPointer;
while(!primeFilter[++lPointer]);
}
else
{
found++;
sum -= lPointer;
while(!primeFilter[++lPointer]);
}
}
std::cout << found;
}
소수를 구한 배열은 배열의 값으로 소수를 찾는게 아니라, 인덱스로 찾게 되므로 R L 값을 증가시킬 때 해당 배열이 NULL이 아닐때까지 계속 증가시켜주도록 구현하였다.
투 포인터 문제중에 스탠다드 접근인 것 같다. 조금 꼬아진 문제를 이제 풀어봐야겠다.
오늘도 평온한 하루가 되길. 슨민.
'짜잘한 기록' 카테고리의 다른 글
백준 14572 스터디 그룹 (0) | 2021.09.16 |
---|---|
백준 16161 가장 긴 증가하는 팰린드롬 부분수열 (0) | 2021.09.15 |
백준 9527 1의 개수 세기 (0) | 2021.09.13 |
백준 1987 알파벳 (0) | 2021.09.11 |
백준 3109 빵집 (0) | 2021.09.10 |