http://codevs.cn/problem/2319/
题意:给定笛卡尔平面内的若干个点,求最近两点的距离和最远两点的距离,距离是欧氏距离,点数不大于100,000。
对于最近点对考虑分治的做法:用一条直线将所有点分成两部分,对于两部分的点进行分治,然后考虑两部分联合的结果。
可以先把所有点按纵坐标排序,这样就容易用平行于x轴的直线划分。设两边分别求出的结果的较小值是d,我们只需考虑直线两侧离直线距离d以内的所有点即可。
考虑枚举直线某一侧的所有点,对于每个点,在直线另一侧到该点距离小于d的点,是有常数性的限度的。
假设这个点离直线非常非常近,或者就在直线上,以该点为圆心,d为半径所作出的圆在直线另一侧的部分是一个劣弓形,或者半圆。
直线另一侧的一个点若要到该点的距离小于d,则必须落在这个弓形或半圆以内。一个小小的半圆里面能有多少个点呢?注意到在这些点中,所有点对的距离不小于d!
最多只有5个点。事实上,具体算法实现非常简单。对前一半点和后一半点进行分治,然后两重循环枚举前一半点和后一半中的前五个点,计算距离并更新即可。
对于最远点对,显然答案中的两个点一定在整个点集的凸包上,凸包可以用Graham算法来求出。然后旋转卡壳即可。
关于旋转卡壳,我们这里要找的就是在凸包上,与每个点距离最远的另一个点,设凸包上的若干个点依次是p[]号点,离p[i]号点最远的点是f[i]号点。
则对于顺时针依次的p[i]和p[i+1],其答案f[i]和f[i+1]也一定按顺时针排列,这是比较显然的,于是可以用单调队列来求出f[],选出最远的一对即可。
代码:
#include<cstdio> #include<iostream> #include<cstring> #include<string> #include<algorithm> #include<map> #include<set> #include<bitset> #include<queue> #include<stack> #include<cmath> #include<climits> #define rpt(i,l,r) for(i=l;i<=r;i++) #define rpd(i,r,l) for(i=r;i>=l;i--); #define maxn 100005 using namespace std; struct point{ double x,y; }; point a[maxn],b[maxn*2]; int n,i; double ans1=INT_MAX,ans2=0; bool cmp1(point A,point B){ return A.y<B.y; } bool clockwise(point A,point B,point C){ return A.x*B.y+B.x*C.y+C.x*A.y-A.y*B.x-B.y*C.x-C.y*A.x<0; } bool cmp2(point A,point B){ return clockwise(A,a[1],B); } double dist(point A,point B){ double distx=A.x-B.x,disty=A.y-B.y; return sqrt(distx*distx+disty*disty); } void input(){ scanf("%d",&n); rpt(i,1,n) scanf("%lf%lf",&a[i].x,&a[i].y); } void solve1(int l,int r){ if(l<r){ int m=l+r>>1,i,j,k; double tmp; solve1(l,m); solve1(m+1,r); rpt(i,l,m) rpt(j,m+1,min(m+5,r)){ tmp=dist(a[i],a[j]); if(tmp<ans1) ans1=tmp; } } } void solve2(){ int k=2,j=1; double tmp; b[1]=a[1],b[2]=a[2]; rpt(i,3,n){ while(k>1&&clockwise(b[k-1],b[k],a[i])) k--; b[++k]=a[i]; } rpt(i,1,k) b[k+i]=b[i]; rpt(i,1,k){ while(dist(b[i],b[j])<dist(b[i],b[j+1])+1e-6) j++; tmp=dist(b[i],b[j]); if(tmp>ans2) ans2=tmp; } } int main(){ input(); sort(a+1,a+n+1,cmp1); solve1(1,n); sort(a+2,a+n+1,cmp2); solve2(); printf("%.2f %.2f\n",ans1,ans2); return 0; }