• 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
    ",ans);
    60     return 0;
    61 }
    62 
    63 int sb=hh();
    64 int main(int argc,char**argv) {;}
    代码


  • 相关阅读:
    【hive】时间段为五分钟的统计
    【git】提交到github不显示贡献小绿点问题的解决
    【hive】关于用户留存率的计算
    【hive】求日期是星期几
    【hive】数据仓库层次设计
    【hive】count() count(if) count(distinct if) sum(if)的区别
    转载:几种 hive join 类型简介
    丑小鸭和白天鹅没有区别
    好好照顾自己,就像照顾你爱的人那样;
    灵光一闪(最近更新于2020/8/23)
  • 原文地址:https://www.cnblogs.com/whistle13326/p/7543526.html
Copyright © 2020-2023  润新知