[BOJ] 3878 점 분리
Post
취소

[BOJ] 3878 점 분리

문제 요약 및 풀이

3878번: 점 분리

사실 1년 전에 푼 문제다.

기하 알고리즘을 갑자기 한번 쓰윽 볼까? 하고 다시 핀 문제인데,

1년동안 회사일을 하면서 다 까먹었음을 느꼈다. 컨벡스헐의 ㅋ 자도 못 구현하고 있었다.

그래서, 그냥 1년 전의 나의 코드를 가져와서 코드를 리팩토링하면서, 다시 공부했다.

풀이 설명은 코드에 주석으로 담았다.(또 다른 미래의 나를 위해)

풀이 코드

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#include <bits/stdc++.h>

#define for1(s,n) for(int i = s; i<n; i++)
#define for1j(s,n) for(int j = s; j<n; j++)
#define foreach(k) for(auto i : k)
#define foreachj(k) for(auto j : k)
#define pb(a) push_back((a))
#define sz(a) a.size()

using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef vector <int> iv1;
typedef vector <vector<int> > iv2;
typedef vector <ll> llv1;
typedef unsigned int uint;
typedef vector <ull> ullv1;
typedef vector <vector <ull> > ullv2;

struct point{
  ll x, y, p=0, q=0;

  bool operator <(point b){
      if(x != b.x) {
          return x < b.x;
      }
      return y < b.y;
  };
  bool operator ==(point b) {
    return y == b.y && x == b.x;
  }
  bool operator <=(point b){
    if(y == b.y && x == b.x) return true;
    if(x != b.x) {
      return x <= b.x;
    }
    return y <= b.y;
  };
};

ll N, M, tc;
vector <point> black;
vector <point> white;

ll ccw(point p1, point p2, point p3) {
  ll ret = 1ll * (p1.x * p2.y + p2.x * p3.y + p3.x * p1.y - p2.x * p1.y - p3.x * p2.y - p1.x * p3.y);
  return ret > 0 ? 1 : (ret < 0 ? -1 : 0);
}

bool comp(point a, point b) {
  if(a.q * b.p != a.p*b.q) return 1ll * a.q * b.p < a.p * b.q;
  if(a.y != b.y) return a.y < b.y;
  return a.x < b.x;
}

void swapPoint(point a, point b) {
  point tmp;
  tmp = a;
  a = b;
  b= tmp;
}

vector <int> getConvexHull(vector <point>& points) {
  vector <int> stk; stk.clear();

  // y 좌표, x 좌표가 작은 순으로 정렬
  sort(points.begin(), points.end(), comp);

  // 0번째 점을 기준으로 상대 위치 계산
  for(int x=1; x<points.size(); x++) {
      points[x].p = points[x].x - points[0].x;
      points[x].q = points[x].y - points[0].y;
  }

  // 문제 특성상, 두 컨벡스 헐이 겹치는지 판단해야 하므로, 같은 점을 2번 반환한다.
  if(points.size() == 1) {
    return {0, 0};
  }

  // 0번째 점을 제외하고, 반시계 방향으로 정렬(0번째 점을 기준으로 한 상대 위치로 하여금)
  sort(points.begin() + 1, points.end(), comp);

  stk.push_back(0);
  stk.push_back(1);

  for(int next = 2; next < points.size(); next++) {
    // 현재 stack에 쌓인 점이 2개 이상일 때,
    while(stk.size() >= 2) {
      int second = stk.back(); stk.pop_back();
      int first = stk.back();
      
      if(ccw(points[first], points[second], points[next]) > 0) {
        // 이전 점 2개와 비교했을 때, 반시계 방향을 향할 때(좌회전), 다시 second를 넣어준다.
        stk.push_back(second);
        break;
      }
    }
    stk.push_back(next);
  }

  // 컨벡스 헐에 담긴 점들의 idx가 return 된다.
  return stk;
}

bool is_intersected_line(point a, point b, point c, point d) {
  ll line_a_b = ccw(a, b, c) * ccw(a, b, d);
  ll line_c_d = ccw(c, d, a) * ccw(c, d, b);

  if(line_a_b == 0 && line_c_d == 0) {
    if(b < a) swapPoint(a, b);
    if(d < c) swapPoint(c, d);
    return (c <= b && a <= d);
  }
  return line_a_b <= 0 && line_c_d <= 0;
}

void init() {
  ll x, y;

  cin >> N >> M;

  black.clear();
  white.clear();

  for1(0, N) {
    cin >> x >> y;
    black.push_back({ x, y, 0, 0});
  }
  for1(0, M) {
    cin >> x >> y;
    white.push_back({x, y, 0, 0});
  }
}

int main() {
  ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);

  cin >> tc;

  while(tc--) {
    init();

    if(N==1 && M ==1) {
      /* 각각 점이 한 개씩 있는 경우, 두 그룹으로 나눌 수 있다. */
      cout <<"YES\n";
      continue;
    }

    vector <int> black_covex_hull = getConvexHull(black);
    vector <int> white_convex_hull = getConvexHull(white);
    
    bool invalid = false;
    int black_size = black_covex_hull.size();
    int white_size = white_convex_hull.size();

    // convex hull의 각 선분별로 서로 겹치는지 검사한다.
    for1(0, black_size) {
      for1j(0, white_size) {
        point a,b,c,d;
        a = black[black_covex_hull[i]];
        b = black[black_covex_hull[(i+1) % black_size]];
        c = white[white_convex_hull[j]];
        d = white[white_convex_hull[(j+1) % white_size]];
        if(is_intersected_line(a, b, c, d)) {
          invalid = true;
          break;
        }
      }
      if(invalid) break; 
    }

    /* 아래 경우는 교차하지 않는 두 컨벡스 헐임을 고려해야 한다. */

    bool black_convex_hull_include_white_point = true;
    bool white_convex_hull_include_black_point = true;

    // white 점이 black convex hull 안에 포함되어 있는지 검사한다.
    for1(0, black_size) {
      for1j(0, white_size) {
        point a, b, c;
        a = black[black_covex_hull[i]];
        b = black[black_covex_hull[(i+1) % black_size]];
        c = white[white_convex_hull[j]];
        
        // 하나의 하얀색 점이아도 시계 방향을 향하는 점이라면,
        // white convex hull이 black convex hull에 포함되지 않는다.
        if(ccw(a, b, c) <= 0) {
          black_convex_hull_include_white_point = false;
          break;
        }
      }
      if(!black_convex_hull_include_white_point) break;
    }
    
    if(black_convex_hull_include_white_point) invalid = true;

    // black 점이 white convex hull 안에 포함되어 있는지 검사한다.
    for1(0, white_size) {
      for1j(0, black_size) {
        point a, b, c;
        a = white[white_convex_hull[i]];
        b = white[white_convex_hull[(i+1) % white_size]];
        c = black[black_covex_hull[j]];

        // 하나의 검은색 점이아도 시계 방향을 향하는 점이라면,
        // black convex hull이 white convex hull에 포함되지 않는다.
        if(ccw(a, b, c) <= 0) {
          white_convex_hull_include_black_point = false;
          break;
        }
      }
      if(!white_convex_hull_include_black_point) break;
    }

    if(white_convex_hull_include_black_point) invalid = true;

    cout << (invalid ? "NO" : "YES") << "\n";
  }
}

This post is licensed under CC BY 4.0 by the author.