该题一直TLE,后来把qsort改成sort就过了,悲伤呀,该题用到DP中的分治法,先看看这题分治法的原理:
<!--[if !supportLists]-->1、<!--[endif]-->问题综述
最接近点对问题的提法是:给定平面上n个点,找其中的一对点,使得在n个点组成的所有点对中,该点对间的距离最小。
实际情况下,最接近点对可能多于一对,为简单起见 ,我们只找其中的一对作为问题的解。有一个最直观的方法就是将每一点与其他n-1个点的距离算出,找出达到最小距离的两点即可。然而,这样做效率太低,我们想到用递归法来解决这个问题。
将所给的平面上n个点的集合S分成两个子集S1和S2 ,每个子集中约有n/2个点 。然后在每个子集中递归地求其最接近的点对。在这里,一个关键的问题是如何实现递归法中的合并步骤,即由S1 和S2的最接近点对,如何求得原集合S中的最接近点对。如果组成S的最接近点对的两个点都在S1中或都在S2中,则问题很明显就可以找到答案。可是还存在另外一种可能,就是这两给点分别在S1和S2 中的时候。下面主要讨论这种情况。
为使问题易于理解和分析,我们先来考虑一维的情形。此时,S中的n各点退化为x轴上的n个实数x1,x2,x3…xn。最接近点对即为这n个实数中相差最小的两个实数。我们尝试用递归法来求解,并希望推广到二维的情形。
假设我们用x轴上的某个点m将S划分为两个集合S1和S2 ,使得S1={x∈S | x≤m};
S2 ={ x∈S | x>m }。这样一来,对于所有p∈和q∈S2 有p<q。
递归地在S1和S2 上找出其最接近点对{p1,p2}和{q1,q2},并设
d=min{| p1- p2|,| q1- q2|}
由此易知,S中的最接近进点对或者是{ p1 ,p2},或者是{ q1 ,q2},或者是某个{p3,q3},其中,p3∈S1且q3∈S2 。
s1 |m s2
___._______._________._____.________|_______._________.______.______._____._____
p1 p2 p3 q3 q2 q1
我们注意到,如果S的最接近点对是{p3,q3},即|p3-q3|<d,则p3和q3两者与m的距离不超过d,即| p3-m|<d,| q3 -m|<d。也就是说 ,p3∈(m-d,m],q3∈(m,m+d]。由于每个长度为d的半闭区间至多包含S1中的一个点,并且m是S1和S2 的分割点,由图2-1-1可以看出,如果(m-d,m]中有S中点 ,则此点就是S1中最大点。同理,如果(m,m+d]中有S中点,则此点就是S2 中最小点。因此,我们用线性时间就能找到区间(m-d,m]和(m,m+d]中所有点,即p3和q3。从而我们用线性时间就可以将S1的解和S2 的解合并成为S的解。
但是,还有一个问题需要认真考虑,即分割点m的选取,即S1和S2 的划分。选取分割点m的一个基本要求是由此将S进行分割,使得S= S1∪S2 ,S1≠Φ,S2 ≠Φ,且S1∈{x|x≤m },S2 ∈{x| x>m }。容易看出,如果选取m={max(S)+min(S)}/2,可以满足分割要求。然而,这样选取分割点,有可能造成划分出的子集S1和S2 的不平衡 。例如在最坏情况下,| S1|=1,| S2 |=n-1,这样的计算效率与分割前相比提高幅度不明显。这种现象可以通过递归法中“平衡子问题”的方法加以解决。我们可以适当选择分割点m,使S1和S2 中有个数大致相等的点。我们会想到用S中各点坐标的中位数来作为分割点。
由此,我们设计出一个求一维点集S的最接近点对的算法Cpair 1如下:
---------------------------------------------------------------
Bool Cpair 1(S,d)
{
N=|S|;
if(n<2){
d=∞;
return false;
}
m=S中各点坐标的中位数;
//构造S1和S2 ;
S1={x∈S | x≤m};
S2 ={ x∈S | x>m };
Cpair 1(S1,d1);
Cpair 1(S2 ,d2);
p=max(S1);
q=min(S2 );
d=min(d1,d2,q-p);
return true
}
以上一维算法可以推广到二维的情形。
设S中的点为平面上的点,它们都有两个坐标值x和y。为了将平面上点集S线性分割为大小大致相等的两个子集S1和S2 ,我们选取一垂直线L:x=m来作为分割直线。其中,m为S中各点x坐标的中位数。由此将S分割为S1={p∈S | x (p)≤m }和S2 ={p∈S | x (p) >m }。从而使S1和S2 分别位于直线L的左侧和右侧,且S=S1∪S2 。由于m是S中各x坐标的中位数,因此S1和S2 中得点数大致相等。
递归地在S1和S2 上解最接近点对问题,我们分别得到S1和S2 中的最小距离d1和d2。现设d=min{d1,d2}。若S的最接近点对(p,q)之间的距离小于d,则p和q必分属于S1和S2 。不妨设p∈S1,q∈S2 。那么p和q距直线L的距离均小于d。因此,若我们用P1和P2分别表示直线L的左边和右边的宽为d的两个垂直长条区域,则p∈P1且q∈P2。
#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-d,m]和(m,m+d]中最多各有S中一个点。因而这两点成为唯一的未检查过的最接近点对候选者。二维的情形则要复杂些。此时,P1中所有点和P2中所有点构成的点对均为最接近点对的候选者。在最坏的情况下有n2/4对这样的候选者。考虑P1中任意一点p,它若与P2中的点q构成最接近点对的候选者,则必有distance(p,q)<d。满足这个条件的P2中的点有多少个呢?容易看出这样的点一定落在一个d*2d的矩形R中