볼록 껍질 (Convex Hull)

구현 (Graham's scan)

#include <stdio.h>
#include <vector>
#include <algorithm>

using namespace std;

typedef long long int lld;
lld ccw(struct Point p1, struct Point p2, struct Point p3);

struct Point {
    lld x, y;
    Point *s = NULL;

    bool operator <(const Point &o) const {
        if (s != NULL) {
            lld ret = ccw(*s, *this, o);
            if (ret != 0) return ret > 0;
        }
        if (y != o.y) return y < o.y;
        return x < o.x;
    }
};

lld ccw(struct Point p1, struct Point p2, struct Point p3) {
    return (p2.x - p1.x)*(p3.y - p1.y) - (p2.y - p1.y)*(p3.x - p1.x);
}

Point p[100010];

int main()
{
    int n, i;
    vector<int> s;

    scanf("%d", &n);
    for (i = 0; i < n; i++)
        scanf("%lld%lld", &p[i].x, &p[i].y);

    // 정렬하여 첫 점을 구하고, 다른 값에 첫 점 정보를 입력
    sort(p, p + n);
    for (i = 1; i < n; i++)
        p[i].s = &p[0];

    // 각에 따른 정렬 (반시계 방향)
    sort(p + 1, p + n);

    // 스택을 이용하여 컨벡스헐을 찾음
    s.push_back(0);
    s.push_back(1);
    for (i = 2; i < n; i++) {
        while (s.size() >= 2) {
            int last1 = *(s.end() - 1);
            int last2 = *(s.end() - 2);
            if (ccw(p[last2], p[last1], p[i]) > 0)
                break;
            s.pop_back();
        }
        s.push_back(i);
    }

    printf("%d", s.size());

    return 0;
}

1708 문제 풀이

// 2차원 평면상에 있는 점들을 모두 포함하는 볼록 다각형을 볼록 껍질(convex hull)이라고 한다.
// 전체 시간복잡도는 정렬을 하는 데 필요한 O(NlogN)

#include <stdio.h>
#include <algorithm>
#include <vector>
using namespace std;

struct vec{
    double x, y;
    vec operator -(const vec& rhs) const { return {x-rhs.x, y-rhs.y}; }
    bool operator <(const vec &rhs) const {
        if(y != rhs.y) return y < rhs.y;
        return x < rhs.x;
    }
    double cross(const vec& rhs) const { return x * rhs.y - rhs.x * y; }
};
double ccw(vec a, vec b){ return a.cross(b); }
double ccw(vec p, vec a, vec b){ return ccw(a-p, b-p); }

// 각정렬을 위한 기준점 s
// 각정렬 시 기준점 기준으로 일직선인 경우를 처리 안 해주면 오답이 난다.
// 일직선인 경우 기준점에 더 가까운 점이 정렬 시 더 앞쪽으로 오는데 
// 그렇게 해야지만 (더 멀리 있는 점을 선택하면서) 알고리즘이 올바르게 작동한다.
// 처리를 안 하면 일직선인 점 2개 중 어떤 게 앞에 올 지는 데이터에 따라 다르기 때문에
// 디버깅에서 고생할 수 있다.
vec s;
bool cmp(vec a, vec b){
    if(ccw(a-s, b-s) != 0) return ccw(a-s, b-s) > 0;
    return a < b;
}

vector<int> convex_hull(vector<vec>& p){
    sort(p.begin(), p.end());
    s = p[0];
    sort(p.begin()+1, p.end(), cmp);

    vector<int> ret;
    ret.push_back(0);
    ret.push_back(1);

    for(int now=2; now<p.size(); now++){
        while(ret.size() >= 2){
            int last1 = *(ret.end() - 1);
            int last2 = *(ret.end() - 2);
            // 이상적이다.
            if(ccw(p[last2], p[last1], p[now]) > 0) break;
            ret.pop_back();
        }
        ret.push_back(now);
    }
    return ret;
}

용준이가 작성

정리

  • Graham's scan : O(NlogN)
  • 정렬하여 오른쪽 아래의 점을 찾는다 (y점이 가장작고, y가 같다면 x점이 가장 작은 한 점)
  • ccw 함수를 통해서 (첫점-현재-비교 가 반시계방향으로) 정렬한다.
  • 스택을 이용해서, 이전 두개와 다음점이 반시계 방향이 아닐 경우 스택에서 제외하여, 모든 점이 반시계 방향이 되도록 넣는다.
  • 스택에 들어간 점은 컨벡스헐을 이루고 있다.

관련 문제

  • 1708번: 볼록 껍질
  • 6850번: Cows
  • 17403번: 가장 높고 넓은 성
  • 2254번: 감옥 건설 (★)
  • 7420번: 맹독 방벽
  • 3878번: 점 분리 (★)
  • 9240번: 로버트 후드 (★)
  • 10254번: 고속도로 (★)

참고 자료

results matching ""

    No results matching ""