CS/Algorism

[백준] 2467. 용액

olsohee 2024. 1. 19. 10:44

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

 

2467번: 용액

첫째 줄에는 전체 용액의 수 N이 입력된다. N은 2 이상 100,000 이하의 정수이다. 둘째 줄에는 용액의 특성값을 나타내는 N개의 정수가 빈칸을 사이에 두고 오름차순으로 입력되며, 이 수들은 모두 -

www.acmicpc.net

이 문제는 이분탐색으로도 풀 수 있고, 투포인터로도 풀 수 있다.

이분탐색

  • 수를 두개씩 더해서 그 절댓값이 가장 작은 것이 답이 된다.
  • 이때 이중 반복문으로 수를 더하면 O(N^2)의 시간 복잡도가 발생한다. 따라서 이분탐색으로 풀어야 한다. (시간 복잡도: O(N * logN))
  • 배열을 정렬한 후 특정 인덱스를 기준으로, 해당 인덱스보다 큰 인덱스들을 범위로 이분탐색하여, 더한 값의 절댓값이 가장 작은 수를 계속 갱신해나가면 된다.
  • 이때 주의할 점은 특정 인덱스를 기준으로 할 때, 해당 인덱스보다 작거나 같은 인덱스는 탐색할 필요없다. 예를 들어 -99, 98이나 98, -99나 같기 때문에 중복을 제거하는 것이다.
  • 그리고 두 수의 합이 0보다 크면 왼쪽 범위로 탐색 범위를 옮기고, 두 수의 합이 0보다 작으면 오른쪽 범위로 탐색 범위를 옮기면 된다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

// 시간 복잡도: O(N * logN)
public class Main {

    static int n;
    static int[] arr;
    static long answer = Long.MAX_VALUE;
    static int[] answerNumberArr = new int[2];

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n = Integer.parseInt(br.readLine());
        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);

        for (int i = 0; i < n - 1; i++) {
            int minIdx = i + 1;
            int maxIdx = n - 1;

            while (minIdx <= maxIdx) {
                int midIdx = (minIdx + maxIdx) / 2;
                long sum = arr[i] + arr[midIdx];

                if (answer > Math.abs(sum)) {
                    answer = Math.abs(sum);
                    answerNumberArr[0] = arr[i];
                    answerNumberArr[1] = arr[midIdx];
                }

                if (sum <= 0) {
                    minIdx = midIdx + 1;
                } else {
                    maxIdx = midIdx - 1;
                }
            }
        }

        for (int i : answerNumberArr) {
            System.out.print(i + " ");
        }
    }
}

투포인터

  • 투포인터로 풀면 O(N) 시간만에 풀 수 있다.
  • 이분탐색에서의 아이디어와 같다.
  • 우선 시작 점인 left를 0으로, 끝 점임 right를 n-1로 둔다.
  • 그리고 시작 점과 끝 점 사이 값의 합을 구해서, 합의 절댓값이 작으면 갱신한다.
  • 그리고 합이 0보다 크거나 같으면 끝 점을 한 칸 왼쪽으로 옮기고, 합이 0보다 작으면 시작 점을 한 칸 오른쪽으로 옮긴다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

// 시간 복잡도: O(N)
public class Main {

    static int n;
    static int[] arr;
    static long answer = Long.MAX_VALUE;
    static int[] answerNumberArr = new int[2];

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n = Integer.parseInt(br.readLine());
        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);

        int left = 0;
        int right = n - 1;
        while (left < right) {
            int sum = arr[left] + arr[right];
            if (answer > Math.abs(sum)) {
                answer = Math.abs(sum);
                answerNumberArr[0] = arr[left];
                answerNumberArr[1] = arr[right];
            }

            if (sum >= 0) {
                right--;
            } else {
                left++;
            }
        }

        for (int i : answerNumberArr) {
            System.out.print(i + " ");
        }
    }
}

'CS > Algorism' 카테고리의 다른 글

[백준] 2295. 세 수의 합  (1) 2024.01.26
[백준] 2003. 수들의 합2  (0) 2024.01.21
[백준] 18869. 멀티버스 Ⅱ  (1) 2024.01.18
[백준] 16401. 과자 나눠주기  (0) 2024.01.18
[프로그래머스] 조이스틱  (1) 2024.01.13