-
3878 점분리 (라이브러리 사용 x)알고리즘/백준 2019. 9. 1. 02:12
1. 문제
https://www.acmicpc.net/problem/3878
3878번: 점 분리
문제 평면 위에 여러 개의 검정 점과 흰 점이 있다. 이때, 길이가 무한대인 직선을 그어 흰 점과 검은 점을 분리하려고 한다. 직선은 어떤 점과도 만나면 안 된다. 직선으로 인해서 나누어지는 두 그룹 중 한 그룹에는 흰 점만 있어야 하고, 다른 그룹에는 검은 점만 있어야 한다. 아래 그림에서 제일 왼쪽 예제는 점선으로 표시된 직선으로 두 점을 나눌 수 있다. 하지만 나머지 예제는 직선으로 점을 분리할 수 없다. 흰 점과 검은 점의 좌표가 주어졌을 때, 직
www.acmicpc.net
2. 코드
#include <iostream> #define ll long long using namespace std; struct node { ll x = 0, y = 0; ll p = 0, q = 0; node operator - (const node &p) { return { x - p.x, y - p.y, 0, 0 }; } ll cross(const node &p) { return x * p.y - y * p.x; } bool operator < (const node &p) { if (x != p.x) return x < p.x; return y < p.y; } }; node black[105]; node white[105]; bool comp(node p1, node p2) { //반시계 방향으로 정렬 if (p1.q * p2.p != p1.p * p2.q) return p1.q * p2.p < p1.p * p2.q; //x와 y가 작은 순으로 정렬 if (p1.y != p2.y) return p1.y < p2.y; return p1.x < p2.x; } void qsort(node *p, int left, int right) { if (left >= right) return; int l = left - 1; int r = right + 1; node mid = p[(l + r) / 2]; while (1) { while (comp(p[++l], mid)); while (comp(mid, p[--r])); if (l >= r) break; node tmp = p[l]; p[l] = p[r]; p[r] = tmp; } qsort(p, left, l - 1); qsort(p, r + 1, right); } struct STACK { int stack[105]; int size = 0; void push(int n) { stack[size++] = n; } void pop() { size--; } int top() { return stack[size - 1]; } }; int ccw(node p1, node p2, node p3) { ll ans = (p2 - p1).cross(p3 - p2); if (ans > 0) ans = 1; else if (ans < 0) ans = -1; return ans; } bool isCross(node p1, node p2, node p3, node p4) { int ab = ccw(p1, p2, p3)*ccw(p1, p2, p4); int cd = ccw(p3, p4, p1)*ccw(p3, p4, p2); //일직선 상에 있는 선분들이 겹치는지 겹치지 않는지 검사 if (ccw(p1, p2, p3) == 0 && ccw(p1, p2, p4) == 0) { if (p2 < p1) swap(p1, p2); if (p4 < p3) swap(p3, p4); return !(p2 < p3 || p4 < p1); } return ab <= 0 && cd <= 0; } //점 a가 b안에 있는지 검사 bool isPointInside(STACK &s, node *a, node *b) { if (s.size <= 2) return false; int prevDir = ccw(b[s.stack[0]], b[s.stack[1]], a[0]); for (int i = 1; i < s.size; i++) { int nowDir = ccw(b[s.stack[i]], b[s.stack[(i + 1) % s.size]], a[0]); if (prevDir != nowDir) return false; } return true; } //모든 점들을 반시계 방향으로 정렬(y축이 같다면 x값이 작은 순) void sortCCW(node *point, int num) { qsort(point, 0, num - 1); for (int i = 1; i < num; i++) { point[i].p = point[i].x - point[0].x; point[i].q = point[i].y - point[0].y; } qsort(point, 1, num - 1); } void convexHull(STACK &s, node *point, int num) { if(num > 0) s.push(0); if(num > 1) s.push(1); int next = 2; while (next < num) { while (s.size >= 2) { int second = s.top(); s.pop(); int first = s.top(); if (ccw(point[first], point[second], point[next]) > 0) { s.push(second); break; } } s.push(next++); } } bool polygonIntersects(STACK &a, STACK &b) { if (isPointInside(b, black, white) || isPointInside(a, white, black)) return true; for (int i = 0; i < a.size; i++) { for (int j = 0; j < b.size; j++) { if (isCross(black[a.stack[i]], black[a.stack[(i + 1) % a.size]], white[b.stack[j]], white[b.stack[(j + 1) % b.size]])) return true; } } return false; } int main() { int t; cin >> t; while (t--) { int n, m; cin >> n >> m; for (int i = 0; i < 105; ++i) { black[i].p = black[i].q = white[i].p = white[i].q = 0; } for (int i = 0; i < n; i++) { cin >> black[i].x >> black[i].y; } for (int i = 0; i < m; i++) { cin >> white[i].x >> white[i].y; } if (n <= 1 && m <= 1) { cout << "YES\n"; continue; } STACK bs; sortCCW(black, n); convexHull(bs, black, n); STACK ws; sortCCW(white, m); convexHull(ws, white, m); if (polygonIntersects(bs, ws)) cout << "NO\n"; else cout << "YES\n"; } }
3. 후기
점들을 분리하기 위해선 각 점들의 볼록껍질을 구해서 모든 변마다 교차하는지 검사하면 된다. 교차하는지 검사하기 전에 한 볼록껍질이 다른 볼록껍질 안에 들어가 있는지도 검사를 해야 하는데 한 점만 검사하면 된다. 그 이유는 한 점이 다른 볼록껍질 안에 있다면 모든 점이 볼록껍질 안에 있을 수도 있고, 다른 몇 개의 점들은 밖에 있을 수도 있지만 교차한다. 따라서 두 경우 모두 점을 분리할 수 없다.
'알고리즘 > 백준' 카테고리의 다른 글
3860 할로윈 묘지 (라이브러리x) (0) 2019.11.10 1764 듣보잡(라이브러리 사용x) (0) 2019.11.10 2094 강수량 (라이브러리 사용 x) (0) 2019.09.01 15683 감시 (0) 2019.05.11 16197 두 동전 (0) 2019.05.10