문제 풀이/백준

[백준] 6588번. 골드바흐의 추측(JAVA)

27200 2025. 6. 13. 09:45

문제

https://www.acmicpc.net/problem/6588


풀이(25분)

import java.io.*;
import java.util.*;

public class Main {

    static List<Integer> primeLists = new ArrayList<>();
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int n = Integer.parseInt(br.readLine());
        dijkstra(); // 소수 리스트 파악
        StringBuilder sb = new StringBuilder(); // 정답 출력을 위한 객체 생성

        while(n != 0){
            int endIdx = binarySearch(n); // 효율적 탐색을 위한 최종 인덱스 설정
            int[] num = twoPointer(n, endIdx); // 정답 탐색
            if(num == null){ // 정답이 없다면
                sb.append("Goldbach's conjecture is wrong.\n");
                continue;
            }
            sb.append(n).append(" = ").append(num[0]).append(" + ").append(num[1]).append("\n");
            n = Integer.parseInt(br.readLine());
        }
        System.out.println(sb.toString());
    }

    static int binarySearch(int target){
        int start = 0;
        int end = primeLists.size() - 2;
        int mid = (start + end) / 2;
        while(start <= end){
            mid = (start + end) / 2;
            if(primeLists.get(mid) > target){
                end = mid - 1;
                continue;
            }
            if(primeLists.get(mid) < target){
                start = mid + 1;
            }
        }
        return mid + 1;
    }

    static int[] twoPointer(int target, int endIdx){
        int start = 1;
        int end = endIdx;
        while(start <= end){;
            int startNum = primeLists.get(start);
            int endNum = primeLists.get(end);

            if(target ==  startNum + endNum){ // 목표에 다 왔다면
                return new int[]{startNum, endNum};
            }
            if(target <  startNum + endNum){ // 원하는 값보다 작다면
                end--;
                continue;
            }
            start++;
        }
        return null;
    }

    static void dijkstra(){
        boolean[] isPrime = new boolean[1_000_001];
        Arrays.fill(isPrime, true);
        isPrime[0] = false;
        isPrime[1] = false;
        for(int i = 2; i <= Math.sqrt(1_000_001); i++){
            if(!isPrime[i]){
                continue;
            }
            for(int j = i * i; j <= 1_000_000; j += i){
                isPrime[j] = false;
            }
        }
        // 이제 소수만 리스트에 추가
        for (int i = 2; i <= 1_000_000; i++) {
            if (isPrime[i]) {
                primeLists.add(i);
            }
        }
    }
}

문제 풀이 전략

 

실버 1 문제치고 많은 개념이 들어갔다.

 

일단 문제에서 짝수 = 소수 + 소수로 나타낼 수 있다고 한다. 물론 홀수인 소수지만 2를 제외하면 모두 홀수이므로 그 부분은 넘어가자.

💡 그렇다면 우선되는 것은 소수를 찾아야 한다. -> 에라토스테네스의 체를 이용하자.

 

[도구정리] 에라토스테네스의 체

에라토스테네스의 체는 소수를 판별해야할 때 매우 유리한 알고리즘이다. 일반적오르 소수를 판별하는 방법은 1을 제외한 자기자신까지를 나눠보며 1과 자기자신으로만 나누어 떨어지는 것이

to-travel-coding.tistory.com


소수를 모두 찾았다면, 이제 어떤 두 소수의 합이 원하는 짝수를 만들어내는지 찾으면 된다.

💡 투포인터를 이용하여 원하는 인덱스와 값을 찾아낼 것이다.

만약 그렇지 못한다면 null을 반환하여 해당하는 문구를 출력해 주면 된다.

 

[도구정리] 투포인터

투포인터란? 인덱스를 두 개로 두어 O(N^2)이 될 수 있는 탐색을 O(N)으로 만들어주는 것이다.예를 들어보자. 연속된 숫자가 존재하는 배열(1,2,3,4,5,1,2,3,4,5,) 같이 존재한다. 이때 연속된 수열의 합

to-travel-coding.tistory.com


하지만 막무가내로 투포인터를 사용하면 100만 이하의 소수가 78000개정도인데 너무 많지 않을까?

-> 투포인터는 특성상 인덱스를 확 줄이지 않기 때문에 꽤 큰 부담이 될 수 있다.

💡 이분탐색을 통해 끝 인덱스를 효율적으로 찾아내자.

이분탐색을 통해 최종 인덱스를 찾아 그 부분에서부터 줄여나간다면 더욱 효율적인 연산이 가능할 것이다.

 

[도구정리] Binary Search(이분탐색/이진탐색)

개념이진탐색은 정렬된 배열에서만 사용 가능한 탐색법이다.변수 3개(start, end, mid)를 사용하여 탐색한다. 찾으려는 데이터와 중간점 위치에 있는 데이터를 반복적으로 비교해서 원하는 데이터

to-travel-coding.tistory.com


최종적으로 모든 결과들을 통합해 답을 출력하면 된다!