设P={(x1,y1),(x2,y2),⋯,(xn,yn)}是平面上散列的n个点的集合。请编写程序找出集合中距离最近的点对。严格地说,相同距离的最近点对可能不止一对,为了简单期间只找出第一对最近点对即可。题目保证输入的所有数据均按照横坐标值进行升序排列。
输入格式:
输入第一行给出一个正整数n,表示平面上点的数数量。随后n行,每行给出一个实数对,每个实数对表示一个点的坐标值,其中第1数表示横坐标,第2数表示纵坐标。
输出格式:
输出最近点对中两个点的坐标和它们之间的距离。如果 x1+y1<=x2+y2
则按
(x1,y1),(x2,y2),miniDist=Distance
输出结果,否则按
(x2,y2),(x1,y1),miniDist=Distance
输出结果。
其中x1
,y1
,x2
,y2
是保留两位小数的实数,Distance
是保留3位小数的实数
输入样例:
5
-1.00 2.00
0.00 2.00
0.50 0.60
1.00 1.00
2.00 0.00
输出样例:
(0.50,0.60),(1.00,1.00),miniDist=0.640
代码长度限制16 KB
时间限制15 ms
内存限制64 MB
答案:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define MAXN 200001
double min(double a, double b) { return a < b ? a : b; }
int n, temp[MAXN];
struct point {
double x, y;
friend bool operator < (point p1, point p2) {
if (p1.x == p2.x) return p1.y < p2.y;
return p1.x < p2.x;
}
}p[MAXN];
bool cmp(int a, int b) {
return p[a].y < p[b].y;
}
double getd(int x, int y) {
return sqrt((p[x].x - p[y].x) * (p[x].x - p[y].x) + (p[x].y - p[y].y) * (p[x].y - p[y].y));
}
struct closest_pair {
double dist;
point p1, p2;
}cp;
closest_pair merge(int l, int r) {
closest_pair res;
res.dist = 2147483647.0;
if (l == r) {
res.p1 = p[l];
res.p2 = p[l];
return res;
}
if (l == r - 1) {
res.dist = getd(l, r);
if (p[l].x + p[l].y <= p[r].x + p[r].y) {
res.p1 = p[l];
res.p2 = p[r];
}
else {
res.p1 = p[r];
res.p2 = p[l];
}
return res;
}
int mid = (l + r) >> 1;
closest_pair d1 = merge(l, mid);
closest_pair d2 = merge(mid + 1, r);
if (d1.dist < d2.dist) {
res = d1;
}
else {
res = d2;
}
int cnt = 0;
for (int i = l; i <= r; ++i) {
if (fabs(p[mid].x - p[i].x) < res.dist) {
temp[++cnt] = i;
}
}
std::sort(temp + 1, temp + cnt + 1, cmp);
for (int i = 1; i <= cnt; ++i) {
for (int j = i + 1; j <= cnt && p[temp[j]].y - p[temp[i]].y < res.dist; ++j) {
double d = getd(temp[i], temp[j]);
if (d < res.dist) {
res.dist = d;
if (p[temp[i]].x + p[temp[i]].y <= p[temp[j]].x + p[temp[j]].y) {
res.p1 = p[temp[i]];
res.p2 = p[temp[j]];
}
else {
res.p1 = p[temp[j]];
res.p2 = p[temp[i]];
}
}
}
}
return res;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%lf %lf", &p[i].x, &p[i].y);
}
std::sort(p + 1, p + n + 1);
cp = merge(1, n);
printf("(%.2lf,%.2lf),(%.2lf,%.2lf),miniDist=%.3lf\n", cp.p1.x, cp.p1.y, cp.p2.x, cp.p2.y, cp.dist);
return 0;
}
反馈结果:
思路: 分治
首先,定义了一个point
结构体来表示一个点,包含两个属性:x和y坐标。重载了小于号运算符,以便在排序时使用。
然后,实现了一个cmp
函数用于排序。这个函数根据点的y坐标进行比较,用于后面的排序操作。
接下来,实现了一个getd
函数,用于计算两个点之间的距离。
然后,实现了一个merge
函数,用于合并和计算最小距离。这个函数采用分治的思想,将点集分成左右两部分,分别计算左右两部分的最小距离,然后再计算两部分之间的最小距离,并返回最小值。
最后,在main
函数中,读取输入的点集,对点集进行排序,然后调用merge
函数计算最小距离,并输出结果。
在merge
函数中,对于当前的左右区间,首先计算左右区间各自的最小距离 d1
和 d2
,然后取其中较小的一个作为初始最小距离 d
。
接下来,通过遍历左边区间的点,在横坐标距离小于 d
的点集中,以纵坐标排序,并存储到 temp
数组中。这是为了后续在纵坐标差小于 d
的点对中进行最小距离的计算。
然后,遍历 temp
数组中的点,计算每对点之间的距离,并更新最小距离 d
。
最后,将计算得到的最小距离返回。
在 main
函数中,首先读取输入的点集,然后对点集进行排序,接着调用 merge
函数计算最小距离,并以保留4位小数的形式输出结果。
总结来说,temp
数组的排序操作是为了将左边区间中横坐标距离小于 d
的点按照纵坐标进行排序,以便后续计算纵坐标差小于 d
的点对的最小距离。这个排序操作是分治算法计算最小距离的一部分。