地址:http://acm.hdu.edu.cn/showproblem.php?pid=1007
思路:最近点对+分治
对于点集a[n]先按x坐标从小到大排序,取中点a[n/2]将其分成左右两边进行分治,那么最近点对分为三种情况:
1.最近点对都在左边 resL;
2.最近点对都在右边 resR;
3.最近点对一个在左边,一个在右边 resH。
那么答案res为三种情况中的最小值,前两种情况按照分治思想可以处理,主要是第三种情况的处理。
首先分治求出 res=min(resL,resR),对于resH,可以遍历左边点集的点去找右边点集的最近点对,有已知现在最小的最近点对值res,因此对于左边点集的点aLi,比res小的其右边点集的最近点对只可能在一个 (2res) X res的矩形内
由于虚线分成的6个矩形的对角线长度为
5
6
r
e
s
\frac{5}{6}res
65res ,若该矩形内有两个点,那么其距离就小于res,而res本就是左右两边最近点对距离的最小值,因此与res矛盾,故 (2res) X res的矩形内最多只有6个点
因此可以对右边点集 x<a[n/2]+res的点按y坐标轴从小到大排序,然后遍历左边点集aLi,在 aLi.y-res到 aLi.y+res的范围内找即可
Code:
#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<double,double> pr;
const int MAX_N=1e5+5;
int n;
pr a[MAX_N];
bool cmp(const pr &A,const pr &B){
if(A.second==B.second) return A.first<B.first;
return A.second<B.second;
}
double Find(int l,int r){
if(l==r) return 1e16;
int h=(l+r)/2,ri,k;
double res=min(Find(l,h),Find(h+1,r));
ri=lower_bound(a+h+1,a+r+1,pr{a[h].first+res+0.01,0})-a;
sort(a+h+1,a+ri,cmp);
if(ri==n) --ri;
for(int i=l;i<=h;++i)
{
k=lower_bound(a+h+1,a+ri,pr{0,a[i].second-res-0.01},cmp)-a;
while(k<=ri&&a[i].second+res>=a[k].second){
res=min(res,sqrt(pow(a[i].first-a[k].first,2)+pow(a[i].second-a[k].second,2)));
++k;
}
}
return res;
}
int main()
{
while(scanf("%d",&n)&&n){
for(int i=0;i<n;++i)
scanf("%lf%lf",&a[i].first,&a[i].second);
sort(a,a+n);
printf("%.2lf\n",Find(0,n-1)/2);
}
}