본문 바로가기

온라인 코딩/수학(Math)

[백준] 17386번 선분 교차 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
#include<iostream>
 
using ll = long long;
struct Point {
    ll x;
    ll y;
};
 
ll CCW(Point A, Point B, Point C) {
    ll result = (B.x - A.x) * (C.y - A.y) - (C.x - A.x) * (B.y - A.y);
 
    if (result > 0) {
        return 1;
    }
    else if (result < 0) {
        return -1;
    }
    return 0;
}
 
int main() {
    Point A, B, C, D;
 
    //L1== AB
    //L2== CD
    std::cin >> A.x >> A.y >> B.x >> B.y;
    std::cin >> C.x >> C.y >> D.x >> D.y;
 
    ll ABC = CCW(A, B, C);
    ll ABD = CCW(A, B, D);
    ll CDA = CCW(C, D, A);
    ll CDB = CCW(C, D, B);
 
 
    if ((ABC * ABD) == 0 && (CDA * CDB) == 0) {
        ll a, b, c, d;
        a = A.x;
        b = B.x;
        c = C.x;
        d = D.x;
        //B---A
        if (B.x < A.x) {
            a = B.x;
            b = A.x;
        }
        //D---C
        if (D.x < C.x) {
            c = D.x;
            d = C.x;
        }
        if (a <= d && c <= d)
            std::cout << "1\n";
        else
            std::cout << "0\n";
    }
    else if ((ABC * ABD) <= 0 && (CDA * CDB) <= 0) {
        std::cout << "1\n";
    }
    else {
        std::cout << "0\n";
    }
}
 
 

 

후기

 

이 문제를 해결하기 위한 키워드는 CCW이다.

CCW 알고리즘을 사용하면 선분이 겹쳐있는지 알 수 있다. 

이렇게 선분 AB와 선분 CD가 있을 때 선분 AB를 기준으로 CCW(A,B,C) 와 CCW(A,B,D)를 진행하면 하나는 시계 방향 하나는 반 시계 방향이 나오기 때문에 교차여부를 확인할 수 있다.  하지만 이러한 방법도 한계가 존재한다.

바로 위와 같은 경우이다. 이 경우에는 서로 교차하지 않지만 CCW(A,B,C)와 CCW(A,B,D)를 진행하면 시계와 반시계 방향이기 나오기 때문에 교차판정이 나오게 된다. 이것을 해결하는 방법은 비교적 어렵지 않다. CCW(A,B,C) 와 CCW(A,B,D)를 진행한거와 동일하게 CD에도 CCW(C,D,A) CCW(C,D,B)연산을 진행하는 것이다.

이렇게 진행 후 CCW(A,B,C)* CCW(A,B,D) 와 CCW(C,D,A)*CCW(C,D,B)를 곱한 후 두개가 모두 0보다 같거나 작을 경우가 교차를 하는 경우이다.

1
2
3
4
    ll ABC = CCW(A, B, C);
    ll ABD = CCW(A, B, D);
    ll CDA = CCW(C, D, A);
    ll CDB = CCW(C, D, B);

 

이렇게 진행을 하여도 한개의 반례 사례가 존재한다.

이 경우에는 아래는 교차하고 위는 교차하지 않지만 둘다 0이 나오기 때문에 따로 비교 판단을 진행해야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
if ((ABC * ABD) == 0 && (CDA * CDB) == 0) {
        ll a, b, c, d;
        a = A.x;
        b = B.x;
        c = C.x;
        d = D.x;
        //B---A
        if (B.x < A.x) {
            a = B.x;
            b = A.x;
        }
        //D---C
        if (D.x < C.x) {
            c = D.x;
            d = C.x;
        }
        if (a <= d && c <= d)
            std::cout << "1\n";
        else
            std::cout << "0\n";
    }
 

이 코드가 바로 해당 반례를 진행한 코드이다.

중간에 값을 바꾸는 코드가 있는대 이 코드는 위와 같이 항상 A가 B보다 왼쪽에 있다는 보장이 없기 때문에 아래와 같은 경우를 처리하기 위한 코드이다. 

 

문제를 진행하면서 도움이 되는 좌표찍기 웹사이트(그래픽 계산기) 링크를 첨부한다.

 

출처 및 레퍼런스

문제 링크: https://www.acmicpc.net/problem/17386

그래픽 계산기 링크: https://www.geogebra.org/graphing 

그림: 본인

 

관련 글

[자료구조 알고리즘/기타 알고리즘(Other)] - CCW(Counter Clock Wise)

[온라인 코딩/수학(Math)] - [백준] 11758번 CCW

 

 

'온라인 코딩 > 수학(Math)' 카테고리의 다른 글

[백준] 1475번 방 번호  (0) 2020.07.17
[프로그래머스] 콜라츠 추측  (0) 2020.06.26
[백준] 11758번 CCW  (0) 2020.04.23
[백준] 4504번 배수 찾기  (0) 2020.04.11
[백준] 2581번 소수  (0) 2020.04.03