平面最近点对
Overview¶
Given
Here we will introduce a divide and conquer algorithm with time complexity of
Algorithm¶
Like the regular divide-and-conquer algorithm, we split this set of
We first sort all points according to
And recursively, find the nearest point pair within these two point sets and assume the distances are
Now it's time to merge! We are trying to find such a set of point pairs, one of which belongs to
For each point
If we sort the points in
From this we have the steps of merging:
- Build the collection
B - Sort the points in
B y_i O(n\log n) O(n) - For each
p_i \in B p_j \in C(p_i) (p_i,p_j)
Please note that we mentioned two sorts above, because the point coordinates remain the same throughout. The first sorting can be done only once before the divide and conquer begins. We make each recursive return to the result of the current point set sorted by
It seems that this algorithm is still not optimal,
Proof of complexity¶
We have learned that the vertical coordinates of all points in
We then split this rectangle into two squares of
We split a square of
Therefore, there are at most
Implementation¶
We use a structure to store points and define a function object for sorting:
Structure definition
struct pt {
int x, y, id;
};
struct cmp_x {
bool operator()(const pt& a, const pt& b) const {
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
};
struct cmp_y {
bool operator()(const pt& a, const pt& b) const { return a.y < b.y; }
};
int n;
vector<pt> a;
To facilitate recursion, we introduce the upd_ans()
helper function to calculate the distance between two points and try to update the answer:
function to update answer
double mindist;
int ansa, ansb;
inline void upd_ans(const pt& a, const pt& b) {
double dist =
sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + .0);
if (dist < mindist) mindist = dist, ansa = a.id, ansb = b.id;
}
Here is the recursion itself: suppose that a[]
has been sorted by
We use std::merge()
to perform the merge sort and create an auxiliary buffer t[]
in which
main function
void rec(int l, int r) {
if (r - l <= 3) {
for (int i = l; i <= r; ++i)
for (int j = i + 1; j <= r; ++j) upd_ans(a[i], a[j]);
sort(a + l, a + r + 1, &cmp_y);
return;
}
int m = (l + r) >> 1;
int midx = a[m].x;
rec(l, m), rec(m + 1, r);
static pt t[MAXN];
merge(a + l, a + m + 1, a + m + 1, a + r + 1, t, &cmp_y);
copy(t, t + r - l + 1, a + l);
int tsz = 0;
for (int i = l; i <= r; ++i)
if (abs(a[i].x - midx) < mindist) {
for (int j = tsz - 1; j >= 0 && a[i].y - t[j].y < mindist; --j)
upd_ans(a[i], t[j]);
t[tsz++] = a[i];
}
}
In the main function, start recursion like this:
Function call interface
sort(a, a + n, &cmp_x);
mindist = 1E20;
rec(0, n - 1);
Generalization: the smallest perimeter triangle on the plane¶
The above algorithm is interestingly extended to this problem: in a given set of points, select three points so that the sum of their distances is the smallest.
The algorithm remains largely unchanged. Each time it tries to find a triangle smaller than the current answer perimeter
Non-divide and conquer algorithm¶
In fact, in addition to the divide and conquer algorithm mentioned above, there is another non-divide and conquer algorithm whose time complexity is also
We can consider the idea of a common statistical sequence: for each element, add the contribution of itself and all the elements to its left into the answer. This way of thinking can also be used to solve the plane closest points problem.
Specifically, we sort all points according to
- Delete all points satisfying
x_i-x_j >= d - For all points in the set satisfying
\lvert y_i - y_j \rvert < d p_i - Insert
p_i
Since each point will be inserted and deleted at most once, the time complexity of inserting and deleting points is
Template code
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <set>
const int N = 200005;
int n;
double ans = 1e20;
struct point {
double x, y;
point(double x = 0, double y = 0) : x(x), y(y) {}
};
struct cmp_x {
bool operator()(const point &a, const point &b) const {
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
};
struct cmp_y {
bool operator()(const point &a, const point &b) const { return a.y < b.y; }
};
inline void upd_ans(const point &a, const point &b) {
double dist = sqrt(pow((a.x - b.x), 2) + pow((a.y - b.y), 2));
if (ans > dist) ans = dist;
}
point a[N];
std::multiset<point, cmp_y> s;
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%lf%lf", &a[i].x, &a[i].y);
std::sort(a, a + n, cmp_x());
for (int i = 0, l = 0; i < n; i++) {
while (l < i && a[i].x - a[l].x >= ans) s.erase(s.find(a[l++]));
for (auto it = s.lower_bound(point(a[i].x, a[i].y - ans));
it != s.end() && it->y - a[i].y < ans; it++)
upd_ans(*it, a[i]);
s.insert(a[i]);
}
printf("%.4lf", ans);
return 0;
}
Practice problems¶
- UVA 10245 "The Closest Pair Problem"[difficulty: low]
- SPOJ #8725 CLOPPAIR "Closest Point Pair"[difficulty: low]
- CODEFORCES Team Olympiad Saratov - 2011 "Minimum amount"[difficulty: medium]
- Google CodeJam 2009 Final "Min Perimeter"[difficulty: medium]
- SPOJ #7029 CLOSEST "Closest Triple"[difficulty: medium]
References¶
The divide and conquer algorithm part in this page is mainly translated from the blog post Нахождение пары ближайших точек and its English version Finding the nearest pair of points. The copyright agreement for the Russian version is Public Domain + Leave a Link; the copyright agreement for the English version is CC-BY-SA 4.0.
Zhihu column: computational geometry-nearest point pair problem
buildLast update and/or translate time of this article,Check the history
editFound smelly bugs? Translation outdated? Wanna contribute with us? Edit this Page on Github
peopleContributor of this article OI-wiki
translateTranslator of this article Visit the original article!
copyrightThe article is available under CC BY-SA 4.0 & SATA ; additional terms may apply.