题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1007
题目大意:最近点对。
题目分析:(1)假设有n(n≥3)个点,按x排序,编号为1~n。平均分成两部分,左边为1~m,最小距离为d1;右边为m+1~n,最小距离为d2。
(2)如果最小点对完全存在于左半部分或右半部分,则d=min(d1,d2);如果横跨两半部分,则继续讨论(3)。
(3)易知,符合要求的点到分界线m的距离≤d,我们将此范围内的点保存起来,并按y排序。
(4)思考一下问题:在一个长为2*d,宽为d的矩形区域内,存在x个点,它们两两之间的最小距离不大于d,求x的最大值。
(5)若i点和j点的y坐标之差大于当前最小值,则不再继续比较。由于这样的点最多6个,复杂度为O(1)。整个算法复杂度为O(nlogn),非常快!
代码参考:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 9;
struct Point//储存坐标
{
double x, y;
}p[N], ans[N];
double dis(Point a, Point b)//计算两点之间的距离
{
double x = a.x - b.x, y = a.y - b.y;
return sqrt(x * x + y * y);
}
bool cmpx(Point a, Point b)//按x轴排序
{
return a.x < b.x;
}
bool cmpy(Point a, Point b)//按y轴排序
{
return a.y < b.y;
}
double cloest(int L, int R)
{
if(L + 1 == R) return dis(p[L], p[R]);//如果只有两个点直接求其距离
if(L + 2 == R) {//如果有3个点,求其两两之间的距离,取最小值
double d1 = dis(p[L], p[L + 1]);
double d2 = dis(p[L], p[R]);
double d3 = dis(p[L + 1], p[R]);
return min(d1, min(d2, d3));
}
int mid = (L + R) >> 1;//m为左右两半部分的分界线
double rmin = min(cloest(L, mid), cloest(mid + 1, R));//递归求出两半部分的最小距离
int cnt = 0;//记录符合要求的点的个数
for(int i = L; i <= R; ++ i) {//搜索整个当前区间
if(fabs(p[i].x - p[mid].x) <= rmin) {
ans[cnt ++] = p[i];//把符合要求的点保存在ans数组里
}
}
sort(ans, ans + cnt, cmpy);//按y轴排序
for(int i = 0; i < cnt; ++ i) {//遍历所有符合要求的点
for(int j = i + 1; j < cnt; ++ j) {
if(ans[j].y - ans[i].y >= rmin) break;//若y轴之差大于当前的最小值,停止比较
rmin = min(rmin, dis(ans[i], ans[j]));//更新最小值
}
}
return rmin;
}
int main()
{
int n;
while(~scanf("%d", &n) && n) {
for(int i = 0; i < n; ++ i) {
scanf("%lf%lf", &p[i].x, &p[i].y);
}
sort(p, p + n, cmpx);//按x轴排序
printf("%.2lf\n", cloest(0, n - 1) / 2);
}
return 0;
}