백준 3878 점 분리

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

흰 점과 검은 점이 주어졌을 때 두 점을 분리할 수 있는 직선을 그을 수 있는지를 파악하는 문제이다. 만약 흰 점으로 이루어진 컨벡스헐과 검은 점으로 이루어진 컨벡스헐을 각각 구한다고 하자.

3878_1

그렇다면 두 점들을 분리할 수 없는 경우는 위의 2가지로 나타난다. 첫번째는 하나의 컨벡스헐 집합 내부에 다른 색깔 점이 존재하는 경우이다. 생각해보면 다각형 내부에 점이 존재할 경우 시계반대방향으로 다각형의 선분들에 대해서 내부의 점과 ccw를 하게되면 그 값은 항상 양수가 나올 것이다. 따라서 흰색 컨벡스 헐에 대해 검은색 컨벡스 헐에 속하는 점들을 모두 확인해주고 마찬가지로 색깔을 바꿔서 동일하게 진행하면 내부에 다른 색깔 점이 존재하는지 확인할 수 있다.

두번째는 점이 컨벡스 헐 선분 위에 존재하는 경우이다. 이 경우 ccw 결과가 0이 나오기 때문에 위에서 확인할 수 없다. 만약 ccw결과가 0이 나올 경우에도 분리할 수 없도록 하라! 라고 설정을 하게 되면 세번째 그림과 같은 상황에서 틀린 결과를 얻게 된다. 실제로는 분리할 수 있는데 같은 직선상에 존재한다고 판단해서 ccw값이 0이 나타나게 되기 때문이다. 이를 방지하기 위해 검은색 컨벡스 헐의 모든 선분과 하얀색 컨벡스 헐의 모든 선분 각각에 대해서 충돌여부를 확인해준다.


func ccw(a: (Int, Int), b: (Int, Int), c: (Int, Int)) -> Int {
    let result = (b.0-a.0)*(c.1-a.1) - (b.1-a.1)*(c.0-a.0)
    return result > 0 ? 1 : (result == 0 ? 0 : -1)
}

func dist(a: (Int, Int), b: (Int, Int)) -> Int {
    let dx = a.0 - b.0, dy = a.1-b.1
    return dx*dx + dy*dy
}

func check(a: (Int, Int), set: [(Int, Int)]) -> Bool {
    let count = set.count
    for i in 0..<count {
        if ccw(a: set[i], b: set[(i+1)%count], c: a) <= 0 {
            return false
        }
    }
    return true
}

func notCrash(a1: (Int, Int), a2: (Int, Int), b1: (Int, Int), b2: (Int, Int)) -> Bool {
    var A = a1, B = a2, C = b1, D = b2
    if A > B {
        swap(&A, &B)
    }
    if C > D {
        swap(&C, &D)
    }
    let abc = ccw(a: A, b: B, c: C)
    let abd = ccw(a: A, b: B, c: D)
    let cda = ccw(a: C, b: D, c: A)
    let cdb = ccw(a: C, b: D, c: B)
    if abc == 0 && abd == 0 && cda == 0 && cdb == 0 {
         return B < C || D < A
    }
    return !(abc*abd <= 0 && cda*cdb <= 0)
}

func makeHul(input: [(Int, Int)]) -> [(Int, Int)] {
    var arr = input
    arr.sort(by: {$0.1 < $1.1})
    arr.sort(by: {$0.0 < $1.0})
    let S = arr[0]
    arr[1..<arr.count].sort(by: {
        ccw(a: S, b: $0, c: $1) == 0 && (dist(a: S, b: $0) < dist(a: S, b: $1))
    })
    arr[1..<arr.count].sort(by: {
        ccw(a: S, b: $0, c: $1) > 0
    })
    var stack:[(Int, Int)] = []
    for next in arr {
        while stack.count >= 2 && (ccw(a: stack[stack.count-2], b: stack[stack.count-1], c: next) <= 0) {
            stack.removeLast()
        }
        stack.append(next)
    }
    return stack
}

let t = Int(readLine()!)!
var str = ""
for _ in 0..<t {
    let nm = readLine()!.split(separator: " ").map{Int(String($0))!}
    let n = nm[0], m = nm[1]
    var black:[(Int, Int)] = Array(repeating: (0, 0), count: n)
    var white:[(Int, Int)] = Array(repeating: (0, 0), count: m)
    for i in 0..<n {
        let line = readLine()!.split(separator: " ").map{Int(String($0))!}
        black[i] = (line[0], line[1])
    }
    for i in 0..<m {
        let line = readLine()!.split(separator: " ").map{Int(String($0))!}
        white[i] = (line[0], line[1])
    }
    let blackHul = makeHul(input: black)
    let whiteHul = makeHul(input: white)
    let p = blackHul.count, q = whiteHul.count
    var result = true
    for i in blackHul {
        if check(a: i, set: whiteHul) {
            result = false
            break
        }
    }
    for i in whiteHul {
        if check(a: i, set: blackHul) {
            result = false
            break
        }
    }
    loop: for i in 0..<p {
        for j in 0..<q {
            if !notCrash(a1: blackHul[i], a2: blackHul[(i+1)%p], b1: whiteHul[j], b2: whiteHul[(j+1)%q]) {
                result = false
                break loop
            }
        }
    }
    str += result ? "YES\n" : "NO\n"
}
print(str)