Line Sweep 알고리즘 :: 마이구미
이 글은 Line Sweep 알고리즘을 다룬다.
Line Sweep, Sweep Line, 라인 스위핑 등과 같이 불려진다.
일반적으로 가장 가까운 두 점을 찾는 문제에서 출발한다.
다음 링크의 문제 풀이를 통하여 알고리즘을 설명할 것이다.
수 많은 점들 중에서 가장 가까운 두 점을 찾는 문제가 존재한다.
이러한 문제의 시나리오는 다음과 같다.
See the Pen line sweeping by leejunghyun (@mygumi) on CodePen.
순수한 접근으로 점들 중에서 가장 가까운 두 점을 찾는 방법은 간단하다.
모든 점들을 확인하면 된다.
하지만 이러한 경우 시간복잡도는 O(N^2) 이기에, 많은 시간이 걸린다.
이러한 문제를 해결하기 위해 Line Sweep 이라는 알고리즘을 사용한다.
이 알고리즘은 시간복잡도 O(NlogN) 까지 가능하게 해준다.
한 점(p)을 기준으로 각 p.x, p.y 좌표에서 최단 거리(d)를 -+ 영역에 존재하는 후보들을 추출한다.
나타내는 의미는 다음과 같이 그림으로 표현할 수 있다.
위와 같이 후보들을 추출하는 과정 속에서 최단 거리(d) 를 갱신한다.
이러한 과정을 반복하게 된다.
기본적인 아이디어는 x, y 축을 나누어서 생각한 방식이다.
즉, 우선 x축을 먼저 고려한 후, y축을 고려하는 것이다.
전체적인 순서는 다음과 같다.
- x 값이 증가하는 순으로 정렬한다.
- 두 점(array[0], array[1]) 사이의 거리를 최단 거리라고 가정한다.
- x축의 차이가 최단 거리보다 크다면 비교할 필요가 없기에 후보자를 걸러준다.
- y축을 기준으로 정렬된 후보자들을 통해 최단 거리를 갱신하게 된다.
4번의 과정인 최단 거리를 갱신하기 위한 과정에 있어, 후보자들 사이에서 (y - d) ~ (y + d) 영역을 구해야한다.
이 과정에서 시간 절약을 위해 이분 탐색을 사용하여 찾게 된다.
C++의 경우에는 lower_bound, upper_bound 를 통해 쉽게 구현이 가능하다.
JAVA의 경우에는 조금 더 복잡하지만 TreeSet 을 통해 구현할 수 있다.
본인의 경우에는 TreeSet의 tailSet 메소드를 Comparable 을 통해 오버라이딩했다.
// min => y - d, max => y + d public int compareTo(Point p) { return min < p.y && max > p.y ? 1 : -1; }
Github - https://github.com/hotehrud/acmicpc/blob/master/algorithm/line-sweep/2261.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | private void solve() { int n = sc.nextInt(); ArrayList<Point> list = new ArrayList<Point>(); TreeSet<Point> candidate = new TreeSet<Point>(new ComparatorSet()); int ans = 0; for (int i = 0; i < n; i++) { list.add(new Point(sc.nextInt(), sc.nextInt())); } Collections.sort(list, new ComparatorDescending()); candidate.add(list.get(0)); candidate.add(list.get(1)); ans = distance(list.get(0), list.get(1)); int start = 0; for (int i = 2; i < n; i++) { Point now = list.get(i); // x axis while (start < i) { Point pivot = list.get(start); int x = pivot.x - now.x; if (x * x > ans) { candidate.remove(pivot); start += 1; } else { break; } } int d = (int) Math.sqrt((double) ans) + 1; Point lower_point = new Point(now.y - d, now.y + d); SortedSet<Point> lower = candidate.tailSet(lower_point); Iterator<Point> it_lower = lower.iterator(); while (it_lower.hasNext()) { Point p = it_lower.next(); d = distance(now, p); if (d < ans) { ans = d; } } candidate.add(list.get(i)); } System.out.println(ans); } public static int distance(Point p1, Point p2) { return (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y); } static class ComparatorDescending implements Comparator<Point> { public int compare(Point p1, Point p2) { if (p1.x < p2.x) { return -1; } else if (p1.x == p2.x) { return 0; } else { return 1; } } } static class ComparatorSet implements Comparator<Point> { public int compare(Point p1, Point p2) { if (p1.y == p2.y) { if (p1.x < p2.x) { return -1; } else if (p1.x == p2.x) { return 0; } else { return 1; } } else { return p1.y < p2.y ? 1 : -1; } } } static class Point implements Comparable<Point> { int x; int y; Point(int x, int y) { this.x = x; this.y = y; } public int compareTo(Point p) { return x < p.y && y > p.y ? 1 : -1; } } | cs |