본문 바로가기
백준

[1708번] 볼록 껍질(Convex Hull)

by 2744m 2019. 8. 13.

링크 : https://www.acmicpc.net/problem/1708

대표적인 기하알고리즘 문제

풀이 방법은 보통 3가지가 있다.

1. Jarvis March 알고리즘 : O(NH)

1) 주어진 정점에서 극단점 중 하나를 찾는다.

(극단점 : 볼록 외피의 정점이 되는 점, 보통 x좌표나 y좌표가 제일 크거나 작은점을 선택한다. 이 점들은 반드시 극단점에 포함된다.)

2) 다음 극단점 찾기 : 현재까지 찾은 마지막 극단점 다음에 올 극단점을 모든 점을 보면서 탐색

3) 찾은 극단점이 처음 찾은 극단점일 떄 까지 반복

2. Graham Scan 알고리즘 : O(NlogN)

1) 시작점으로 사용할 극단점 선택

2) 시작점을 (0,0)기준으로 모든 점을 평행이동.

3) 각 점들을 각도 정렬해서 각도가 적은 순서대로 방문

4) CCW(마지막 직전점, 마지막 점, 새로운 점)을 해서 오목관계인지 확인하고 오목관계이면 새로운 점을 제외하고 다음 점으로 CCW

5) 반복

3. Monotone Chain 알고리즘 : O(NlogN)


Graham Scan알고리즘에서 시작점을 원점으로 이동한 만큼 평행하는 이유는 각도 정렬할 때, tan 공식을 이용해서 각도를 구하기 위해서다.

두 점A(x1,y1), B(x2,y2)의 각도를 구하는 공식은  θ = atan(y2-y1/x2-x1) 인데 한 점이 원점이면 계산이 편해진다.

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
#include <iostream>
#include <math.h>
#include <algorithm>
#define MAX 100002
#define px(i) p[i].first
#define py(i) p[i].second
#define pa(i) p[i].angle
using namespace std;
typedef long long ll;
struct P { ll first, second; double angle; };
 
int N;
P p[MAX];
P ST[MAX];
 
ll ccw(P A, P B, P C) {
    ll tmp1 = A.first*B.second + B.first*C.second + C.first*A.second;
    ll tmp2 = B.first*A.second + C.first*B.second + A.first* C.second;
    if (tmp1 - tmp2 < 0)return -1;
    else if (tmp1 - tmp2 > 0)return 1;
    else return 0;
}
 
bool comp1(const P &A, const P &B) {
    if (A.first < B.first) return true;
    else if (A.first == B.first) return A.second < B.second;
    else return false;
}
 
bool comp2(const P &A, const P &B) {
    if (A.angle < B.angle) return true;
    else if (A.angle == B.angle) {
        if(A.first == B.first) return A.second < B.second;
        else return A.first < B.first;
    }
    else return false;
}
 
 
int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> N;
    for (int i = 1; i <= N; i++) {
        cin >> px(i) >> py(i);
    }
    sort(p+1, p + N+1, comp1);
    P origin = p[1];
    for (int i = 1; i <= N; i++) {
        px(i) -= origin.first;
        py(i) -= origin.second;
        pa(i) = atan2(py(i), px(i));
    }
    
    sort(p + 2, p + N+1, comp2);
 
    int nn_p = 1;
    int top = 0;
    while (nn_p <= N) {
        while (top > 1 && ccw(ST[top - 1], ST[top], p[nn_p]) <= 0
            top--;
        ST[++top] = p[nn_p++];
    }
    cout << top;
    return 0;
}
 
cs


'백준' 카테고리의 다른 글

[2749번] 피보나치 수3  (0) 2020.04.04
[1922번] 네트워크 연결  (0) 2019.08.15
[11758번] ccw  (0) 2019.08.13
[2133번] 타일 채우기  (0) 2019.06.11
[2206번] 벽 부수고 이동하기  (0) 2019.05.27

댓글