Programming

[BOJ] 2261 가장 가까운 두 점

minigb 2022. 3. 23. 18:40

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

 

2261번: 가장 가까운 두 점

첫째 줄에 자연수 n(2 ≤ n ≤ 100,000)이 주어진다. 다음 n개의 줄에는 차례로 각 점의 x, y좌표가 주어진다. 각각의 좌표는 절댓값이 10,000을 넘지 않는 정수이다. 여러 점이 같은 좌표를 가질 수도

www.acmicpc.net

오...
신기하다
스위핑 공부 중
풀이 보고 공부했다.

 

포인트는
1. x 좌표 기준으로 스위핑, 그러므로 y 좌표에 대한 건 범위와 관계없이 모두 다 저장해둬야 한다. 지금은 범위에 포함되지 않아도 나중에는 포함될 수 있기 때문에.
2. set은 y 좌표 기준으로 오름차순 정렬되어 있으므로, set에 있는 점의 x 좌표가 범위 안에 있는 점인지를 확인하는 게 필요하다. 이를 위해서 set에 있는 점들을 모두 확인한다면 O(n)이 소요된다. 지금 vector에는 x 좌표 기준 오름차순으로 저장되어 있기 때문에 그걸 이용하면 set 안의 내용 중에 빠져야 하는 걸 확인할 수 있다.

갑자기 궁금했다가 이해된 부분
set에서 lower_bound, upper_bound iterator 구하고 그 범위 내의 점을 모두 확인하면서 최소 거리를 구할 때 최악은 O(n) 아닌가? 했는데 그렇지 않다.

기타 사소한 거
set의 정렬 기준 설정하려고 structure 선언하면서 operator overloading 할 때 const 잘 써야 한다.

코드에서 틀렸던 부분
1. ++index 하면서 그 점을 set에서 빼야 하는데 빠뜨려서 한 번 틀렸다.
2. 각 점을 확인하고 나서 set에 넣어야 하는데 그것도 안 했다. 이건 예제 만들어서 넣어보다가 발견했다.
뭐하냐고!

struct Point {
	int x, y;
	Point() {}
	Point(int x, int y) {
		this->x = x;
		this->y = y;
	}
};

bool x_ascending_order(Point a, Point b) {
	if (a.x != b.x) {
		return a.x < b.x;
	}
	else {
		return a.y < b.y;
	}
}

struct y_ascending_order {
	bool operator() (const Point& a, const Point& b) const {
		if (a.y != b.y) {
			return a.y < b.y;
		}
		else {
			return a.x < b.x;
		}
	}
};

int GetDistSquare(Point a, Point b) {
	return pow(a.x - b.x, 2) + pow(a.y - b.y, 2);
}

int Solution(vector<Point>& point_vector, const int& n) {
	sort(point_vector.begin(), point_vector.end(), x_ascending_order);

	set<Point, y_ascending_order> available_points;
	int min_dist_square = GetDistSquare(point_vector[0], point_vector[1]);

	available_points.insert(point_vector[0]);
	available_points.insert(point_vector[1]);
	int point_in_set_index = 0;
	for (int i = 2; i < n; ++i) {
		const Point& cur = point_vector[i];
		while (point_in_set_index < i) {
			const Point& check = point_vector[point_in_set_index];
			if (pow(check.x - cur.x, 2) > min_dist_square) {
				available_points.erase(check);
				++point_in_set_index;
			}
			else {
				break;
			}
		}

		auto low_iter = available_points.lower_bound(Point(cur.x, cur.y - sqrt(min_dist_square) - 1));
		auto high_iter = available_points.upper_bound(Point(cur.x, cur.y + sqrt(min_dist_square) + 1));

		for (auto iter = low_iter; iter != high_iter; ++iter) {
			min_dist_square = min(min_dist_square, GetDistSquare(*iter, cur));
		}
		available_points.insert(cur);
	}

	return min_dist_square;
}

int main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int n; cin >> n;
	vector<Point> arr(n);
	for (int i = 0; i < n; ++i) {
		cin >> arr[i].x >> arr[i].y;
	}
	cout << Solution(arr, n) << kEndl;
}