티스토리 뷰

알고리즘/백준

백준 1708 볼록 껍질

4567은 소수 2022. 3. 4. 01:25

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

 

1708번: 볼록 껍질

첫째 줄에 점의 개수 N(3 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N개의 줄에 걸쳐 각 점의 x좌표와 y좌표가 빈 칸을 사이에 두고 주어진다. 주어지는 모든 점의 좌표는 다르다. x좌표와 y좌표의 범

www.acmicpc.net

그라함 스캔 알고리즘을 이용해 볼록 껍질 문제를 해결하였습니다.

 

처음 접하는 알고리즘이라 아래 글을 참고하였습니다.

참고 : https://www.crocus.co.kr/1288    

 

볼록 껍질 : Convex hull , 주어진 점들 중 어떤 점을 이어야 모든 내각이 180도 이하가 될 지 구하는 문제입니다.

즉, 껍질처럼 감싸게 되면 주어진 점들로 이루어진 선분은 모두 껍질 안에 포함되게 됩니다.

 

그라함 스캔 알고리즘으로 O(n log n)에 해결이 가능합니다.

 

그라함 스캔 알고리즘

 

1. 전처리 :

1) y좌표, x좌표가 가장 작은 점을 기준점으로 잡는다.

2) 기준점으로부터 반시계 방향으로 다른 점을 정렬한다. (기준점까지의 상대 좌표를 이용)

 

여기서 기준점까지 상대 좌표를 이용해 반시계 방향으로 다른 점을 정렬할 때 외적을 이용합니다.

기준점이 되는 점의 상대 좌표가 (0,0) 일 때, (1,1), (-1,1) 을 반시계 방향으로 잡으면 (1,1), (-1,1) 순임을 쉽게 알 수 있지만, 외적을 이용해 일반화 할 수 있습니다.

상대 좌표의 벡터값이 <1,1>, <-1,1> 이므로 두 벡터를 외적하면 1*1 - (1*(-1)) = 2 > 0 이므로 반시계 방향이 올바름을 알 수 있습니다. 

 

외적 공식 : https://blog.naver.com/mindo1103/90103361104     

간단히 이해하자면, 공간벡터 x, y, z 축 모양으로 생기게 만들면 0보다 큰 값, 반대면 0보다 작은 외적 값을 가집니다.

<1,1> 벡터를 x축으로 생각하고, <-1,1> 벡터를 y축으로 생각하면 z축이 원래 공간좌표의 x,y,z 축과 동일한 모양이므로 외적은 0보다 큽니다.

반대로 <-1,1>, <1,1> 벡터를 외적했다고 치면, <-1,1>을 x축, <1,1>을 y축으로 생각하면 기존 x,y,z 좌표계와 동일한 모양을 가지려면 z축이 뒤집어져야 합니다. 그러므로 이 떄는 외적값이 음수입니다.

 

2. 그라함 스캔 알고리즘

 

기준점을 first, 다음 점을 second 라고 하고, 스택에 넣습니다. 

그리고 다음 점들을 대상으로 ccw 알고리즘을 적용해 좌회전하는지 (외적이 0보다 큰지)를 판단하면 됩니다.

ccw 가 0보다 크다면 다음 점이 second, second가 first 가 되며, 0보다 작다면 그 다음 점을 대상으로 판단합니다.

이를 한 바퀴 돌 때까지 반복하면 됩니다.

 

(우회전 하는 경우면 그 각은 결국 내각이 180도 보다 크게 되는 경로)

 

코드는 다음과 같습니다.

#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>

using namespace std;

typedef long long ll;

typedef struct info {
	ll x, y; // 실제 위치
	ll p, q; // 기준점으로부터 상대 위치
}info;

int n;
vector<info>dots;
stack<int>st;

void init()
{
	cin >> n;
	for (int i = 0; i < n; i++) {
		ll x, y;
		cin >> x >> y;
		dots.push_back({ x, y, (ll)0, (ll)0 });
	}
}

bool compare_yx(const info& dot1, const info& dot2)
{
	// y좌표, x좌표 작은 순으로 정렬
	if (dot1.y != dot2.y) {
		return dot1.y < dot2.y;
	}
	else {
		return dot1.x < dot2.x;
	}
}

bool compare_counter_clock(const info& dot1, const info& dot2)
{
	// 기준점으로부터 반시계 방향 정렬
	if (dot1.p * dot2.q != dot1.q * dot2.p) { 
		// 기준점 (0,0) 잡았을 때 dot1, dot2 벡터를 외적한 걸로
		// 반시계 방향 판단
		// dot1 x dot2 > 0 => dot1, dot2 순
		// dot1 x dot2 = 0 => 일직선
		// dot1 x dot2 < 0 => dot2, dot1 순
		return dot1.p * dot2.q - dot1.q * dot2.p > 0;
	}
	else { 
		// 일직선인 경우
		// y, x좌표 작은 순으로 
		return compare_yx(dot1, dot2);
	}
}

void sortingDots()
{
	// y좌표, x좌표 순으로 점 정렬
	sort(dots.begin(), dots.end(), compare_yx);

	// 기준점 (0번 점) 으로부터 상대 위치 계산
	ll baseX = dots[0].x;
	ll baseY = dots[0].y; 

	for (int i = 1; i < dots.size(); i++) {
		dots[i].p = dots[i].x - baseX;
		dots[i].q = dots[i].y - baseY;
	}

	// 기준점으로부터 반시계방향 정렬
	vector<info>::iterator iter = dots.begin();
	iter++;
	sort(iter, dots.end(), compare_counter_clock);
}

bool CCW(const info& dot1, const info& dot2, const info& dot3)
{
	// 외적으로 좌회전, 우회전 판단
	// dot1, dot2, dot3 순으로 input
	// dot1 to dot2 vector v1
	// dot2 to dot3 vector v2
	// v1 x v2 > 0 => 좌회전
	// v1 x v2 < 0 => 우회전
	// v1 x v2 = 0 => 직진

	// 그라함 스캔 알고리즘에서는 ccw 좌회전 하는 경우 ( > 0 )
	// 추가 진행하므로 ccw > 0 이면 true

	return dot1.x * dot2.y + dot2.x * dot3.y + dot3.x * dot1.y - (dot1.y * dot2.x + dot2.y * dot3.x + dot3.y * dot1.x) > 0;
}

void graham()
{
	int first = 0;
	int second = 1;
	int next = 2;

	st.push(first);
	st.push(second); // 첫 번째, 두 번째 점 

	while (next < n) { // 점 다 돌 때 까지
		while (st.size() >= 2) { // 다음 점 찾기
			second = st.top();
			st.pop();
			first = st.top();

			// first, second, next ccw > 0 => second 다시 push, break
			// ccw < 0 => 다음 점 찾기

			if (CCW(dots[first], dots[second], dots[next]) == true) {
				st.push(second);
				break;
			}
		}

		// 다음 점 넣기
		st.push(next);
		next++;
	}

	// 최종 볼록 껍질은 스택에 있는 번호 순으로 연결하면 됨
}

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	init();

	sortingDots();

	graham();

	cout << st.size();
}

'알고리즘 > 백준' 카테고리의 다른 글

백준 1067 이동 C++  (0) 2022.03.05
백준 13277 큰 수 곱셈 C++  (1) 2022.03.05
백준 4991 로봇 청소기 C++  (0) 2022.03.01
백준 2151 거울 설치 C++  (0) 2022.02.22
백준 1135 뉴스전하기 C++  (0) 2022.02.22
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2024/12   »
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
글 보관함