P1257 平面上的最接近点对

                                  P1257 平面上的最接近点对

题目描述

给定平面上n个点,找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的。

输入输出格式

输入格式:

 

第一行:n;2≤n≤10000

接下来n行:每行两个实数:x y,表示一个点的行坐标和列坐标,中间用一个空格隔开。

 

输出格式:

 

仅一行,一个实数,表示最短距离,精确到小数点后面4位。

 

输入输出样例

输入样例#1:
3
1 1
1 2
2 2
输出样例#1:
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\n",ans);
60     return 0;
61 }
62 
63 int sb=hh();
64 int main(int argc,char**argv) {;}
代码

 



posted @ 2017-09-18 15:57  拿叉插猹哈  阅读(297)  评论(0编辑  收藏  举报