一年前学长讲这题的时候,没听懂,自己搜解题报告也看不懂,放了一年。如今对分治和递归把握的比一年前更加熟悉,这题也就解决了。
题意:给你一堆点让你求最近两点之间距离的一半,如果用暴力的话O(n*n)明显会超时,那么我们就用分治思想,将所有点按照横坐标x排序,然后取中间的mid,分着求1-mid,mid-n,这样递归求解,递归只需要logn级别就可以完成递归,这有点类似二分思想。
1.我们取左边点对最小和右边点对最小比较,取最小的,记做d
2:但是最近点有可能一个点在左边,一个点在右边,所以要单独考虑。
3:我们for循环遍历left->right的每个点,取出其中横坐标满足到中间mid这个点横坐标之差小于d的点,因为求距离还有将y算上,如果你x都大于d了,就不可能距离小于d
4:但是如果满足这样的点很多的话,还是会超时,那么我们还需要做优化,我们将这些满足3的点再按照y坐标排序,然后再求距离,如果有y坐标差大于d的话,就跳出,因为后面的话都是大于y,不存在更小的,所以虽然是两层for循环,其实复杂度并不高
5:总的来说时间复杂度是O(nlogn)
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <math.h>
using namespace std;
const double eps = 1e-6;
const int MAXN = 100010;
const double INF = 1e20;
struct Point
{
double x,y;
};
double dist(Point a,Point b)
{
return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}
Point p[MAXN];
Point tmpt[MAXN];
bool cmpxy(Point a,Point b)
{
if(a.x != b.x)return a.x < b.x;
else return a.y < b.y;
}
bool cmpy(Point a,Point b)
{
return a.y < b.y;
}
double Closest_Pair(int left,int right)
{
double d = INF;
if(left == right)return d;
if(left + 1 == right)
return dist(p[left],p[right]);
int mid = (left+right)/2;
double d1 = Closest_Pair(left,mid);
double d2 = Closest_Pair(mid+1,right);
d = min(d1,d2);
int k = 0;
for(int i = left; i <= right; i++)
{
if(fabs(p[mid].x - p[i].x) <= d)
tmpt[k++] = p[i];
}
sort(tmpt,tmpt+k,cmpy);
for(int i = 0; i <k; i++)
{
for(int j = i+1; j < k && tmpt[j].y - tmpt[i].y < d; j++)
{
d = min(d,dist(tmpt[i],tmpt[j]));
}
}
return d;
}
int main()
{
int n;
while(scanf("%d",&n)==1 && n)
{
for(int i = 0; i < n; i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
sort(p,p+n,cmpxy);
printf("%.2lf\n",Closest_Pair(0,n-1)/2);
}
return 0;
}