본문 바로가기
PS/BOJ

[자바] 백준 2473 - 세 용액 (java)

by Nahwasa 2024. 3. 20.

문제 : boj2473

 

 

필요 알고리즘

  • 투 포인터 또는 이분 탐색
    • 투 포인터를 쓰거나, 이분 탐색을 써서 풀 수 있는 문제이다.

※ 제 코드에서 왜 main 함수에 로직을 직접 작성하지 않았는지, 왜 Scanner를 쓰지 않고 BufferedReader를 사용했는지 등에 대해서는 '자바로 백준 풀 때의 팁 및 주의점' 글을 참고해주세요. 백준을 자바로 풀어보려고 시작하시는 분이나, 백준에서 자바로 풀 때의 팁을 원하시는 분들도 보시는걸 추천드립니다.

 

 

풀이

  우선 처음 생각난 아이디어는, 어차피 n이 5000이므로 2개를 고정시켜놓고, 나머지 하나의 값을 이분 탐색으로 찾는 방법이었다. 이 경우 O(N^2 * logN). 예를들어 현재 고정시켜둔 2개의 값이 -97와 -2라면, -(-97-2) = 99 이므로 최대한 99랑 비슷한 값을 이분탐색으로 찾는거다. 정확히 해당 값이 아니라, 그 근처의 값을 찾아야하니 99보다 큰 값과 99보다 작은 값 모두 찾아야하고 이전에 고정해둔 2개의 인덱스값과도 일치하지 않아야하니 상당히 구현하기가 귀찮다.

 

  사실 어제 이분 탐색으로 풀려고 했는데 자꾸 맞왜틀을 외치다가 그냥 패스했고, 오늘 아침에 '아 걍 투포인터로 하면 되는데;;' 생각나서 다시 풀어서 몇분만에 바로 맞췄다 ㅋㅋ. 이론상 이분 탐색으로 푸는데 전혀 문제가 없고 그냥 내가 구현을 실수한거겠고, 이분 탐색으로 시간초과도 안 뜰 시간복잡도다보니 투 포인터라는 더 쉽고 효율적인 방법이 있는데도 다른 방법을 어제 바로 생각하지 못하게 됐던 것 같다. 이후엔 주의해야할 것 같다.

 

  아무튼 투 포인터를 사용한 풀이는 1개를 고정시켜두고 나머지 2개를 투 포인터로 찾는 방법이다. 이 경우 O(N^2). 시간상으로도 이득이고, 구현도 훨씬 간편하다. 투 포인터 알고리즘을 알고 있다면 이하 주석만 보면 풀이는 알 수 있을꺼라 생각된다.

for (int base = 0; base < n; base++) {	// 하나의 값을 고정해두고
    int s = 0;	// 투 포인터를 진행한다. 물론 arr[]은 이미 정렬된 상태다.
    int e = n-1;

    while (s<e) {
        if (s == base) {s++; continue;}	// 고정해둔값과 동일하면 안된다.
        if (e == base) {e--; continue;}

        long sum = 0l + arr[base] + arr[s] + arr[e];
        if (Math.abs(sum) < min) {	// 현재까지 찾은 최소값보다 합이 더 작다면
            min = Math.abs(sum);	// 답을 교체한다.
            ans = new int[] {arr[base], arr[s], arr[e]};
        }

        if (sum == 0) {printAns(ans); return;}	// 0은 최소치라 더이상 다른걸 찾아볼 이유가 없다.
        if (sum < 0) {s++; continue;}	// 합이 0 미만이면 합을 더 증가시켜야하니 s++
        e--;	// 0 초과라면 합을 더 감소시켜야하니 e--
    }
}

printAns(ans);

 

 

코드 : github

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1 << 16);

    public static void main(String[] args) throws Exception {
        new Main().solution();
    }

    public void solution() throws Exception {
        int n = Integer.parseInt(br.readLine());
        int[] arr = new int[n];
        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 0; i < n; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
        }

        Arrays.sort(arr);
        long min = Long.MAX_VALUE;
        int[] ans = new int[3];

        for (int base = 0; base < n; base++) {
            int s = 0;
            int e = n-1;

            while (s<e) {
                if (s == base) {s++; continue;}
                if (e == base) {e--; continue;}

                long sum = 0l + arr[base] + arr[s] + arr[e];
                if (Math.abs(sum) < min) {
                    min = Math.abs(sum);
                    ans = new int[] {arr[base], arr[s], arr[e]};
                }

                if (sum == 0) {printAns(ans); return;}
                if (sum < 0) {s++; continue;}
                e--;
            }
        }

        printAns(ans);
    }

    private void printAns(int[] ans) {
        Arrays.sort(ans);
        System.out.println(ans[0] + " " + ans[1] + " " + ans[2]);
    }
}

 

댓글