개념

벡터와 외적

점 A=(xA,yA)A=(x_A,y_A)에서 B=(xB,yB)B=(x_B,y_B)로 향하는 벡터는 AB→=(xB−xA,yB−yA)\overrightarrow{AB}=(x_B-x_A,y_B-y_A)입니다. 두 벡터의 덧셈과 뺄셈은 각 좌표끼리 계산합니다.

평면 벡터 u=(ux,uy)u=(u_x,u_y)와 v=(vx,vy)v=(v_x,v_y)의 외적은 다음 값으로 나타냅니다.

u×v=uxvy−uyvxu\times v=u_xv_y-u_yv_x

절댓값은 두 벡터가 만드는 평행사변형의 넓이이며, 부호는 두 벡터의 회전 방향을 나타냅니다.

세 점의 방향 판별

세 점 AA, BB, CC의 방향은 AB→×AC→\overrightarrow{AB}\times\overrightarrow{AC}의 부호로 판별합니다. 이를 CCW 판별이라고 합니다.

외적의 부호A → B → C 방향
양수반시계 방향
0일직선
음수시계 방향

다음 오목한 1010각형에서 경계를 따라 세 점을 선택해 보겠습니다. 회색 선은 다각형 전체이고, 화살표는 방향을 판별할 세 점의 연결 순서입니다.

A=(1,3)A=(1,3), B=(3,1)B=(3,1), C=(7,2)C=(7,2)이면 AB→=(2,−2)\overrightarrow{AB}=(2,-2), AC→=(6,−1)\overrightarrow{AC}=(6,-1)입니다. 외적은 2×(−1)−(−2)×6=102\times(-1)-(-2)\times6=10이므로 A→B→CA\to B\to C는 반시계 방향입니다.

0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 1 2 3 4 5 6 7 8 9 10 11 12 13 x y A B C D E F G H I J
▲ [그림 1] 10각형의 A → B → C: 외적 10, 반시계 방향

같은 다각형의 E=(12,5)E=(12,5), F=(9,6)F=(9,6), G=(11,10)G=(11,10)을 선택하면 EF→=(−3,1)\overrightarrow{EF}=(-3,1), EG→=(−1,5)\overrightarrow{EG}=(-1,5)입니다. 외적은 (−3)×5−1×(−1)=−14(-3)\times5-1\times(-1)=-14이므로 E→F→GE\to F\to G는 시계 방향입니다. FF는 다각형 안쪽으로 들어간 꼭짓점입니다.

0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 1 2 3 4 5 6 7 8 9 10 11 12 13 x y A B C D E F G H I J
▲ [그림 2] 10각형의 E → F → G: 외적 −14, 시계 방향

변 ABAB 위에 K=(2,2)K=(2,2)를 놓으면 A→K→BA\to K\to B의 외적은 1×(−2)−(−1)×2=01\times(-2)-(-1)\times2=0입니다. 세 점이 일직선에 놓이는지는 선분의 기울기와 관계없이 외적으로 확인할 수 있습니다.

0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 1 2 3 4 5 6 7 8 9 10 11 12 13 x y A B C D E F G H I J K
▲ [그림 3] 기울어진 변 위의 A → K → B: 외적 0

기준점 AA의 좌표를 빼서 두 벡터를 만들면 AA를 원점으로 옮겨 생각할 수 있습니다. 외적의 절댓값은 평행사변형의 넓이이고, 두 벡터가 놓인 순서를 바꾸면 부호가 반대가 됩니다. 이 부호로 직선의 어느 쪽에 점이 있는지를 판정합니다. 곱셈과 뺄셈으로 계산하므로 수직선에서도 기울기를 나누어 구할 필요가 없습니다.

복잡도

고정된 세 점을 기준으로 합니다.

연산시간복잡도공간복잡도
CCW 판별O(1)O(1)추가 O(1)O(1)

예제

문제

세 점을 주어진 순서대로 연결할 때의 방향을 판별하여라.

입력

첫째 줄부터 셋째 줄까지 점 A,B,CA,B,C의 좌표가 순서대로 주어진다. 각 줄에는 해당 점의 xx좌표와 yy좌표가 공백을 사이에 두고 주어진다. 각 좌표는 00 이상 100100 이하의 정수이다.

출력

반시계 방향이면 11, 일직선이면 00, 시계 방향이면 −1-1을 출력한다.

예제 입력

1 3
3 1
7 2

예제 출력

1

예제 코드

C++20

#include <iostream>

using namespace std;

struct point {
    int x, y;
};

int cross(const point& a, const point& b, const point& c) {
    return (b.x - a.x) * (c.y - a.y)
         - (b.y - a.y) * (c.x - a.x);
}

int main() {
    point a, b, c;
    int v;

    cin >> a.x >> a.y;
    cin >> b.x >> b.y;
    cin >> c.x >> c.y;

    v = cross(a, b, c);
    if (v > 0) cout << 1 << '\n';
    else if (v == 0) cout << 0 << '\n';
    else cout << -1 << '\n';
}