[BOJ] 2261 가장 가까운 두 점

2 분 소요

분할 정복 예제

문제

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

문제 풀이

package Baekjoon;

import java.awt.Point;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.StringTokenizer;

/**
 * Created by kyeahen.
 * Title : 2261 가장 가까운 두 점
 * Category : 분할정복
 * Date: 2021/04/14
 */

public class BJ_2261 {

    public static void main(String[] args) throws NumberFormatException, IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;

        int N = Integer.parseInt(br.readLine());
        ArrayList<Point> list = new ArrayList<>();

        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());

            int x = Integer.parseInt(st.nextToken());
            int y = Integer.parseInt(st.nextToken());

            list.add(new Point(x, y));
        }
        Collections.sort(list, (p1, p2) -> Integer.compare(p1.x, p2.x)); // x좌표 기준으로 오름차순 정렬.

        System.out.println(solution(list, 0, N - 1));

    }

    // 두 점 사이 거리의 제곱
    public static int dist(Point p1, Point p2) {
        int x = p1.x - p2.x;
        int y = p1.y - p2.y;

        return (x * x) + (y * y);
    }

    // 완전 탐색으로 가장 가까운 거리 찾기
    static int bruteForce(ArrayList<Point> list, int start, int end) {
        int min = Integer.MAX_VALUE;

        for (int i = start; i < end; i++) {
            for (int j = i + 1; j <= end; j++) {

                int diff = dist(list.get(i), list.get(j));
                min = Math.min(diff, min);
            }
        }

        return min;
    }

    public static int solution(ArrayList<Point> arrList, int start, int end) {
        int n = end - start + 1; //좌표 개수

        // 좌표 개수가 3 이하면 완전탐색으로 가장 가까운 두 점 사이의 거리를 찾기
        if (n <= 3) {
            return bruteForce(arrList, start, end);
        }

        int mid = (start + end) / 2;

        // 중앙선을 기준으로 왼쪽 점들 중 가장 작은 거리 = d1 (1. 가장 가까운 두 점이 왼쪽 영역에 있는 경우)
        // 오른쪽 점들 중 가장 작은 거리 = d2 (2. 가장 가까운 두 점이 오른쪽 영역에 있는 경우)
        // min(d1, d2)를 구하면 된다.
        int d = Math.min(solution(arrList, start, mid), solution(arrList, mid + 1, end));

        //3. 가장 가까운 두 점이 왼쪽과 오른쪽 영역 하나씩 있는 경우 : 중앙선을 기점으로 d만큼 떨어진 점만 본다.
        // - 중앙선을 기준으로 양쪽으로 d 거리 이내에 들어오는 점들만 보면 됨.
        ArrayList<Point> band = new ArrayList<>();

        for (int i = start; i <= end; i++) {
            int tx = arrList.get(mid).x - arrList.get(i).x;

            if (tx * tx < d) { // d 미만이면 추가
                band.add(arrList.get(i));
            }
        }

        Collections.sort(band, (p1, p2) -> Integer.compare(p1.y, p2.y)); //y좌표 기준 오름차순 정렬

        // y좌표를 기준으로 오름차순 정렬된 band의 각 요소(P1, P2, ...)는
        // 자기보다 큰 요소만 보면서 거리를 측정함.
        for (int i = 0; i < band.size() - 1; i++) {
            for (int j = i + 1; j < band.size(); j++) {
                int ty = band.get(j).y - band.get(i).y;

                if (ty * ty < d) { //d 미만
                    d = Math.min(d, dist(band.get(i), band.get(j)));
                } else { //y좌표 기준으로 오름차순 정렬되어있으므로, d보다 거리가 큰 순간이 오면 반복문 종료
                    break;
                }
            }
        }

        return d;
    }

}

TMI

21/04/14: 알고리즘 스터디 문제 풀이🤓

댓글남기기