JAVA 알고리즘/유형 문제
[백준 1644 java 자바]소수의 연속합 - 투포인터(부분합 효율성)
nomoreFt
2022. 10. 18. 23:48
[Gold III] 소수의 연속합 - 1644
성능 요약
메모리: 22888 KB, 시간: 1988 ms
분류
수학(math), 정수론(number_theory), 소수 판정(primality_test), 에라토스테네스의 체(sieve), 두 포인터(two_pointer)
문제 설명
하나 이상의 연속된 소수의 합으로 나타낼 수 있는 자연수들이 있다. 몇 가지 자연수의 예를 들어 보면 다음과 같다.
- 3 : 3 (한 가지)
- 41 : 2+3+5+7+11+13 = 11+13+17 = 41 (세 가지)
- 53 : 5+7+11+13+17 = 53 (두 가지)
하지만 연속된 소수의 합으로 나타낼 수 없는 자연수들도 있는데, 20이 그 예이다. 7+13을 계산하면 20이 되기는 하나 7과 13이 연속이 아니기에 적합한 표현이 아니다. 또한 한 소수는 반드시 한 번만 덧셈에 사용될 수 있기 때문에, 3+5+5+7과 같은 표현도 적합하지 않다.
자연수가 주어졌을 때, 이 자연수를 연속된 소수의 합으로 나타낼 수 있는 경우의 수를 구하는 프로그램을 작성하시오.
입력
첫째 줄에 자연수 N이 주어진다. (1 ≤ N ≤ 4,000,000)
출력
첫째 줄에 자연수 N을 연속된 소수의 합으로 나타낼 수 있는 경우의 수를 출력한다.
풀이
투 포인터로 부분합을 구해서 만족하는 조건의 개수를 카운팅하는 것의 소수버전이다.
그냥 일반 1,2,3,4,5의 배열 -> 소수 배열 로 바뀐 것 뿐이다.
소수 배열을 구해야 하는 조건만 추가 됐다. 부분합 투포인터가 이해가 되지 않는다면 이전 문제들을 참고하시면 된다.
- 먼저 문제의 숫자 Math.sqrt(N) 까지 소수 배열을 구한다.
- 소수는 1과 자기자신만으로 나눠지는 숫자이다.
- 제곱근으로 구하는 이유는 11을 예로 들면, 6 이상의 숫자로 11을 나눠봐야 어차피 나눠지지 않기 때문이다. (나머지가 딱 0으로 나뉘지 않음) 그래서 절반쯤까지 한다.
투포인터 연속합 참고 : https://nomoreft.tistory.com/m/101
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
class Main {
static ArrayList<Integer> arr = new ArrayList<>();
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
arr.add(2);
for (int i = 3; i <= N; i++) {
isPrime(i);
}
int arrSize = arr.size();
int left = 0,right = 0,sum = 0,cnt =0;
while (true) {
if (sum >= N) {
sum -= arr.get(left++);
}else if(right == arrSize) break;
else{
sum += arr.get(right++);
}
if (sum == N) {
cnt++;
}
}
System.out.println(cnt);
}
static void isPrime(int x) {
for (int i = 2; i <= Math.sqrt(x); i++) {
if (x % i == 0) {
return;
}
}
arr.add(x);
}
}