문제


https://www.acmicpc.net/problem/3878


풀이


‘검은 점들과 흰 점들을 직선 하나로 분리할 수 있음’은 ‘검은 점들의 볼록 껍질과 흰 점들의 볼록 껍질이 disjoint하면서 포함 관계가 아님’과 동치이다.

‘두 볼록 껍질이 disjoint함’은 ‘모든 (검은 점들의 볼록 껍질의 한 변, 흰 점들의 볼록 껍질의 한 변) 쌍에 대해, 두 변이 서로 교차하지 않음’과 동치이다.

포함 관계는 한 볼록 껍질에서 아무 점이나 잡고 다른 볼록 껍질의 변들과 CCW를 계산함으로써 알 수 있다.


코드


import sys


def tangent(x, y):
    if x == a:
        return 1, y
    if x > a:
        return 0, (y - b) / (x - a), x
    if x < a:
        return 2, (y - b) / (x - a), x


def isline(x, y, z):
    return (y[1] - x[1]) * (z[0] - y[0]) == (z[1] - y[1]) * (y[0] - x[0])


def isintersect(x1, y1, x2, y2, x3, y3, x4, y4):
    p = (x2 - x1) * (y4 - y3) - (x4 - x3) * (y2 - y1)
    q = (x3 - x1) * (y4 - y3) - (x4 - x3) * (y3 - y1)
    r = (x3 - x1) * (y2 - y1) - (x2 - x1) * (y3 - y1)
    if p:
        return (p * q >= 0) and (abs(p) >= abs(q)) and (p * r >= 0) and (abs(p) >= abs(r))
    else:
        s = (x3 - x1) / (x2 - x1) if y1 == y2 else (y3 - y1) / (y2 - y1)
        t = (x4 - x1) / (x2 - x1) if y1 == y2 else (y4 - y1) / (y2 - y1)
        return q == 0 and r == 0 and (s <= 1 or t <= 1) and (s >= 0 or t >= 0)


for _ in range(int(sys.stdin.readline())):
    n, m = map(int, sys.stdin.readline().split())
    black = [tuple(map(int, sys.stdin.readline().split())) for _ in range(n)]
    white = [tuple(map(int, sys.stdin.readline().split())) for _ in range(m)]

    if n == m == 1:
        print('YES')
    
    elif (n, m) == (2, 1):
        x1, y1 = black[0]
        x2, y2 = black[1]
        if isline(black[0], black[1], white[0]) and ((min(x1, x2) < white[0][0] < max(x1, x2)) or (min(y1, y2) < white[0][1] < max(y1, y2))):
            print('NO')
        else:
            print('YES')
    
    elif (n, m) == (1, 2):
        x1, y1 = white[0]
        x2, y2 = white[1]
        if isline(black[0], white[0], white[1]) and ((min(x1, x2) < black[0][0] < max(x1, x2)) or (min(y1, y2) < black[0][1] < max(y1, y2))):
            print('NO')
        else:
            print('YES')
    
    elif (n, m) == (2, 2):
        if isintersect(*black[0], *black[1], *white[0], *white[1]):
            print('NO')
        else:
            print('YES')
    
    else:
        res = []
        for coord in [black, white]:
            if len(coord) == 1:
                res.append(coord)
                continue

            coord.sort(key=lambda x: (x[1], x[0]))
            a, b = coord.pop(0)
            coord.sort(key=lambda x: tangent(x[0], x[1]))

            stack = [(a, b), coord[0]]
            idx = 0
            while idx < len(coord) - 1:
                idx += 1
                w1, w2 = coord[idx]
                while len(stack) >= 2:
                    u1, u2 = stack[-2]
                    v1, v2 = stack[-1]
                    if (v1 - u1) * (w2 - v2) <= (v2 - u2) * (w1 - v1):
                        stack.pop()
                    else:
                        break
                
                stack.append((w1, w2))
                if (len(stack) >= 3) and (isline(stack[-3], stack[-2], stack[-1])):
                    stack.pop(-2)

            if len(stack) >= 3:
                if isline(stack[-2], stack[-1], stack[0]):
                    stack.pop()
            
            res.append(stack)

        flag = True
        if (n > 1) and (m > 1):
            for i in range(-1, len(res[0]) - 1):
                for j in range(-1, len(res[1]) - 1):
                    if isintersect(*res[0][i], *res[0][i + 1], *res[1][j], *res[1][j + 1]):
                        flag = False
                        break
                if not flag:
                    break

        if flag and (m > 1):
            px, py = res[0][0]
            for i in range(-1, len(res[1]) - 1):
                u1, u2 = res[1][i]
                v1, v2 = res[1][i + 1]
                if (u1 - px) * (v2 - py) < (v1 - px) * (u2 - py):
                    break
            else:
                flag = False

        if flag and (n > 1):
            px, py = res[1][0]
            for i in range(-1, len(res[0]) - 1):
                u1, u2 = res[0][i]
                v1, v2 = res[0][i + 1]
                if (u1 - px) * (v2 - py) < (v1 - px) * (u2 - py):
                    break
            else:
                flag = False

        if flag:
            print('YES')
        else:
            print('NO')

Leave a comment