2024. 4. 8. 13:22ㆍ백준
https://www.acmicpc.net/problem/1644
1644번: 소수의 연속합
첫째 줄에 자연수 N이 주어진다. (1 ≤ N ≤ 4,000,000)
www.acmicpc.net
풀이
필요 알고리즘: 에라토스테네스의 체, 투 포인터
문제의 요구사항은 연속된 소수의 합으로 자연수 N을 나타낼 수 있는지이다. 이중 for
문을 활용하여 \(O(N^{2})\)에 풀 수도 있지만, N의 최댓값 4백만으로는 TLE가 나기 딱 좋으므로 다른 방법을 찾아야 한다. 다행히 연속된 소수의 합이므로 투 포인터로 합계의 범위를 조절하며 확인할 수 있고, 이를 활용하면 \(O(N)\)의 시간 복잡도로 풀 수 있다.
먼저 에라토스테네스의 체를 활용하여 N 이하의 소수를 찾는다. 그 후, 투 포인터 first
, last
를 선언하고(이름은 이렇게 했지만 다 풀고 보니 처음과 마지막이랑은 별로 상관없었다), 모두 0번째 인덱스를 가리키도록 한다. 다음으로 소수의 연속합을 저장하는 변수 sum
을 제일 작은 소수(2)로 초기화한다. 그리고 아래와 같은 과정으로 소수의 연속합이 N인지를 확인한다.
sum
의 값이 N 미만이면last
를 1 증가시킨 뒤 그 인덱스의 값을sum
에 더한다.sum
의 값이 N 이상이면first
인덱스의 값을sum
에 더한 뒤first
의 값을 1 증가시킨다. 추가로sum
과 N의 값이 같으면 출력값을 1 증가시킨다.- 위 과정을
first
와last
가 N 이하의 소수의 배열의 인덱스를 벗어날 때까지 계속한다.
시간 복잡도
에라토스테네스의 체의 시간 복잡도는 \(O(N\log{\log{N}})\)이고, 투 포인터의 시간 복잡도는 \(O(N)\)이다. 따라서 총 시간 복잡도는 \(O(N(\log{\log{N}}+1))\simeq O(N\log{\log{N}})\)이다.
소스코드
#include <iostream>
#include <vector>
using namespace std;
int N, ans;
vector<bool> check(4'000'001, true);
vector<int> primes;
// 소수를 찾는 함수
void checkPrime() {
for(int i = 2; i * i <= N; i++) {
if(!check[i]) continue;
for(int j = i * 2; j <= N; j += i) {
check[j] = false;
}
}
for(int i = 2; i <= N; i++) {
if(check[i]) primes.push_back(i);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> N;
if(N == 1) {
cout << 0;
return 0;
}
checkPrime();
int first = 0, last = 0;
int psize = primes.size();
int sum = primes[0];
primes.push_back(0); // OutOfBounds 오류를 막기 위한 더미 원소 삽입
while(first < psize && last < psize) {
if(sum == N) {
ans++;
sum += primes[++last];
}
else if(sum > N) {
sum -= primes[first++];
}
else {
sum += primes[++last];
}
}
cout << ans;
return 0;
}
last
의 값을 먼저 증가시킨 뒤 그 인덱스를 참조하기 때문에, 소수가 저장된 벡터 primes
의 인덱스를 벗어날 수 있다. 이를 막기 위해 primes
에 추가로 더미 원소를 삽입하였다.
'백준' 카테고리의 다른 글
1309. 동물원 (1) | 2024.04.11 |
---|---|
3151. 합이 0 (0) | 2024.04.10 |
15685. 드래곤 커브 (0) | 2024.04.03 |
15684. 사다리 조작 (0) | 2024.03.30 |
1744. 수 묶기 (1) | 2024.03.23 |