P1257 平面上的最接近点对
题目描述
给定平面上n个点,找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的。
输入输出格式
输入格式:
第一行:n;2≤n≤10000
接下来n行:每行两个实数:x y,表示一个点的行坐标和列坐标,中间用一个空格隔开。
输出格式:
仅一行,一个实数,表示最短距离,精确到小数点后面4位。
输入输出样例
3 1 1 1 2 2 2
1.0000
n^2可以卡过洛谷测评机 但是还是分治好
考虑以下分治算法:
设平面上的点都在点集S中,为了将S线性分割为大小大致相等的2个子集S1和S2,我们选取一垂直线l(方程:x=m)来作为分割直线。其中m为S中各点x坐标的中位数。由此将S分割为S1={p∈S|px≤m}和S2={p∈S|px>m}。从而使S1和S2分别位于直线l的左侧和右侧,且S=S1∪S2 。由于m是S中各点x坐标值的中位数,因此S1和S2中的点数大致相等。 递归地在S1和S2上解最接近点对问题,我们分别得到S1和S2中的最小距离δ1和δ2。现设δ=min (δ1,δ2)。
若S的最接近点对(p,q)之间的距离d(p,q)<δ则p和q必分属于S1和S2。不妨设p∈S1,q∈S2。那么p和q距直线l的距离均小于δ。因此,我们若用P1和P2分别表示直线l的左边和右边的宽为δ的2个垂直长条,则p∈S1,q∈S2
此时,P1中所有点与P2中所有点构成的点对均为最接近点对的候选者。在最坏情况下有n^2/4对这样的候选者。但是P1和P2中的点具有以下的稀疏性质,它使我们不必检查所有这n2/4对候选者。考虑P1中任意一点p,它若与P2中的点q构成最接近点对的候选者,则必有d(p,q)<δ。满足这个条件的P2中的点有多少个呢?容易看出这样的点一定落在一个δ×2δ的矩形R中,
由δ的意义可知P2中任何2个S中的点的距离都不小于δ。由此可以推出矩形R中最多只有6个S中的点。事实上,我们可以将矩形R的长为2δ的边3等分,将它的长为δ的边2等分,由此导出6个(δ/2)×(2δ/3)的矩形。
因此d(u,v)≤5δ/6<δ 。这与δ的意义相矛盾。也就是说矩形R中最多只有6个S中的点。由于这种稀疏性质,对于P1中任一点p,P2中最多只有6个点与它构成最接近点对的候选者。因此,在分治法的合并步骤中,我们最多只需要检查6×n/2=3n对候选者,而不是n^2/4对候选者。
我们只知道对于P1中每个S1中的点p最多只需要检查P2中的6个点,但是我们并不确切地知道要检查哪6个点。为了解决这个问题,我们可以将p和P2中所有S2的点投影到垂直线l上。由于能与p点一起构成最接近点对候选者的S2中点一定在矩形R中,所以它们在直线l上的投影点距p在l上投影点的距离小于δ。由上面的分析可知,这种投影点最多只有6个。因此,若将P1和P2中所有S的点按其y坐标排好序,则对P1中所有点p,对排好序的点列作一次扫描,就可以找出所有最接近点对的候选者,对P1中每一点最多只要检查P2中排好序的相继6个点。
至此,我们用分治法求出平面最接近点对。
1 #include <cmath> 2 #include <cctype> 3 #include <cstdio> 4 #include <algorithm> 5 6 const double INF=0x3f3f3fLL; 7 const int MAXN=200010; 8 9 int n; 10 11 int t[MAXN]; 12 13 struct node { 14 double x,y; 15 friend inline bool operator < (node x,node y) { 16 if(x.x==y.x) return x.y<y.y; 17 return x.x<y.x; 18 } 19 }; 20 node E[MAXN]; 21 22 inline double cal(int i,int j) { 23 double x=(E[i].x-E[j].x)*(E[i].x-E[j].x); 24 double y=(E[i].y-E[j].y)*(E[i].y-E[j].y); 25 return sqrt(x+y); 26 } 27 28 inline bool cmp(int x,int y) { 29 return E[x].y<E[y].y; 30 } 31 32 inline double merge(int l,int r) { 33 double d=INF; 34 if(l==r) return d; 35 if(l+1==r) return cal(l,r); 36 int mid=(l+r)>>1; 37 double disl=merge(l,mid); 38 double disr=merge(mid+1,r); 39 d=disl<disr?disl:disr; 40 int tot=0; 41 for(int i=l;i<=r;++i) 42 if(abs(E[mid].x-E[i].x)<=d) 43 t[++tot]=i; 44 std::sort(t+1,t+1+tot,cmp); 45 for(int i=1;i<=tot;++i) 46 for(int j=i+1;j<=tot&&E[t[j]].y-E[t[i]].y<d;++j) { 47 double dis=cal(t[i],t[j]); 48 d=d<dis?d:dis; 49 } 50 return d; 51 } 52 53 int hh() { 54 scanf("%d",&n); 55 for(int i=1;i<=n;++i) 56 scanf("%lf%lf",&E[i].x,&E[i].y); 57 std::sort(E+1,E+1+n); 58 double ans=merge(1,n); 59 printf("%.4lf ",ans); 60 return 0; 61 } 62 63 int sb=hh(); 64 int main(int argc,char**argv) {;}