HDU 1007 Quoit Design 数学

该题一直TLE,后来把qsort改成sort就过了,悲伤呀,该题用到DP中的分治法,先看看这题分治法的原理:

<!--[if !supportLists]-->1、<!--[endif]-->问题综述

最接近点对问题的提法是:给定平面上n个点,找其中的一对点,使得在n个点组成的所有点对中,该点对间的距离最小。

实际情况下,最接近点对可能多于一对,为简单起见 ,我们只找其中的一对作为问题的解。有一个最直观的方法就是将每一点与其他n-1个点的距离算出,找出达到最小距离的两点即可。然而,这样做效率太低,我们想到用递归法来解决这个问题。

 

2 用递归法解决

将所给的平面上n个点的集合S分成两个子集S1S2  ,每个子集中约有n/2个点 。然后在每个子集中递归地求其最接近的点对。在这里,一个关键的问题是如何实现递归法中的合并步骤,即由S1 S2的最接近点对,如何求得原集合S中的最接近点对。如果组成S的最接近点对的两个点都在S1中或都在S2中,则问题很明显就可以找到答案。可是还存在另外一种可能,就是这两给点分别在S1S2  中的时候。下面主要讨论这种情况。

为使问题易于理解和分析,我们先来考虑一维的情形。此时,S中的n各点退化为x轴上的n个实数x1x2x3xn。最接近点对即为这n个实数中相差最小的两个实数。我们尝试用递归法来求解,并希望推广到二维的情形。

2.1 一维情形下的分析(自己一定要画下图)

假设我们用x轴上的某个点mS划分为两个集合S1S2  ,使得S1={xS | xm};

S2  ={ xS | xm }。这样一来,对于所有pqS2  pq

     递归地在S1S2  上找出其最接近点对{p1p2}{q1q2}并设

d=min{| p1- p2|| q1- q2|}

由此易知,S中的最接近进点对或者是{ p1 p2},或者是{ q1  q2},或者是某个{p3q3},其中,p3S1q3S2

          s1                        |m      s2

___._______._________._____.________|_______._________.______.______._____._____

  p1      p2              p3                q3                     q2     q1

我们注意到,如果S的最接近点对是{p3q3},即|p3-q3|d,则p3q3两者与m的距离不超过d,即| p3-m|d| q3 -m|d。也就是说 ,p3(m-dm]q3(mm+d]。由于每个长度为d的半闭区间至多包含S1中的一个点,并且mS1S2  的分割点,由2-1-1可以看出,如果(m-dm]中有S中点 ,则此点就是S1中最大点。同理,如果(mm+d]中有S中点,则此点就是S2  中最小点。因此,我们用线性时间就能找到区间(m-dm](mm+d]中所有点,即p3q3。从而我们用线性时间就可以将S1的解和S2  的解合并成为S的解。

但是,还有一个问题需要认真考虑,即分割点m的选取,即S1S2  的划分。选取分割点m的一个基本要求是由此将S进行分割,使得S= S1S2  S1ΦS2  Φ,且S1{x|xm },S2  {x| xm }。容易看出,如果选取m={max(S)+min(S)}/2,可以满足分割要求。然而,这样选取分割点,有可能造成划分出的子集S1S2  的不平衡 。例如在最坏情况下,| S1|=1| S2 |=n-1,这样的计算效率与分割前相比提高幅度不明显。这种现象可以通过递归法中“平衡子问题”的方法加以解决。我们可以适当选择分割点m,使S1S2  中有个数大致相等的点。我们会想到用S中各点坐标的中位数来作为分割点。

      由此,我们设计出一个求一维点集S的最接近点对的算法Cpair 1如下:

---------------------------------------------------------------

Bool Cpair 1(S,d)

{

N=|S|;

if(n2){

  d=;

  return false;

}

m=S中各点坐标的中位数;

//构造S1S2 

 S1={xS | xm};

S2  ={ xS | xm };

Cpair 1(S1,d1);

Cpair 1(S2  ,d2);

p=max(S1);

q=min(S2 );

d=min(d1,d2,q-p);

return true

}

2.2 二维情形下的分析

      以上一维算法可以推广到二维的情形。

      S中的点为平面上的点,它们都有两个坐标值xy。为了将平面上点集S线性分割为大小大致相等的两个子集S1S2  ,我们选取一垂直线Lx=m来作为分割直线。其中,mS中各点x坐标的中位数。由此将S分割为S1={pS | x (p)m }S2  ={pS | x (p) m }。从而使S1S2  分别位于直线L的左侧和右侧,且S=S1S2  。由于mS中各x坐标的中位数,因此S1S2  中得点数大致相等。

      递归地在S1S2  上解最接近点对问题,我们分别得到S1S2  中的最小距离d1d2。现设d=min{d1,d2}。若S的最接近点对(p,q)之间的距离小于d,则pq必分属于S1S2  。不妨设pS1qS2  。那么pq距直线L的距离均小于d。因此,若我们用P1P2分别表示直线L的左边和右边的宽为d的两个垂直长条区域,则pP1qP2。

#include<stdio.h>

#include<stdlib.h>

#include<algorithm>

#include<math.h>

using namespace std;

struct T

{

   double x,y;       

}point[100024],ans[100024];

bool cmpx(T a,T b) {return a.x<b.x;}

bool cmpy(T a,T b) {return a.y<b.y;}

inline double dist( T n, T m )

{

       double x=n.x-m.x;

       double y=n.y-m.y;

    return sqrt( x*x+y*y );       

}

inline double  closest( int n, int m )

{

    if( (m-n)==1 )//两个点的情况 

    {

       return dist( point[n],point[m] );    

    }

    if( ( m-n )==2 )//三个点的情况

    {

        double d[3],min;

        d[0]=dist( point[n] , point[m] );

        d[1]=dist( point[n+1],point[ m ] );

        d[2]=dist( point[n],point[ m-1] );

        min=d[0];

        for( int i=1; i<3; i++ )

            if( min>d[i] )

              min=d[i];

        return min;    

    } 

    int k=( n+m )/2;

    double d1,d,mid;

    d1=closest( n,k );

    if(d1==0 )

      return 0;

    d=closest( k+1, m );

    if( d1<d )

       d=d1;

    if( d==0 )

     return d;

    int count=0;

    for( int i=n; i<=m; i++ )//选取到中线距离不超过d的距离 

    {

         if( (point[i].x <point[k].x+d)&&( point[i].x>point[k].x-d  ) )

            ans[count++]=point[i];

    }

    sort( ans,ans + count,cmpy );//对y进行排序 

    for( int i=0;i<count; i++ )

    {

       for( int j=i+1; j<count; j++ )

       {

            if( ans[j].y-ans[i].y<d )

            {

                d1=dist( ans[j],ans[i] );

                if( d1<d )

                 d=d1;    

            }

            else break;     

       }

    }

       

    return d;       

}



int main()

{

    int n;

    while( scanf( "%d",&n ),n )

    {

        for( int i=0; i<n; i++ )

            scanf( "%lf%lf",&point[i].x,&point[i].y );

        sort( point ,point + n ,cmpx );

        printf ( "%.2lf\n",closest( 0, n-1 )/2.0 );      

    }

    return 0;    

}

  

在一维情形下,距分割点距离为d的两个区间(m-dm](mm+d]中最多各有S中一个点。因而这两点成为唯一的未检查过的最接近点对候选者。二维的情形则要复杂些。此时,P1中所有点和P2中所有点构成的点对均为最接近点对的候选者。在最坏的情况下有n2/4对这样的候选者。考虑P1中任意一点p,它若与P2中的点q构成最接近点对的候选者,则必有distance(p,q)d。满足这个条件的P2中的点有多少个呢?容易看出这样的点一定落在一个d*2d的矩形R

你可能感兴趣的:(design)