題目描述
給出二維平面上的n個點,求其中最近的兩個點的距離的一半。
輸入包含多組數據,每組數據第一行為n,表示點的個數;接下來n行,每行一個點的坐標。當n為0時表示輸入結束,每組數據輸出一行,為最近的兩個點的距離的一半。
輸入樣例:
2
0 0
1 1
2
1 1
1 1
3
-1.5 0
0 0
0 1.5
0
輸出樣例:
0.71
0.00
0.75
題目解析:
采用分治的思想,把n個點按照x坐標進行排序,以坐標mid為界限分成左右兩個部分,對左右兩個部分分別求最近點對的距離,然后進行合並。對於兩個部分求得的最近距離d,合並過程中應當檢查寬為2d的帶狀區間是否有兩個點分屬於兩個集合而且距離小於d,最多可能有n個點,合並時間最壞情況下是O(n^2).但是,左邊和右邊中的點具有以下稀疏的性質,對於左邊中的任意一點,右邊的點必定落在一個d*2d的矩形中,且最多只需檢查6個點(鴿巢原理),這樣,先將帶狀區間的點按照y坐標進行排序,然后線性掃描,這樣合並的時間復雜度為O(nlogn)。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
double MAX=1e10; //定義的最大距離,以在只有一個點的時返回無窮大
int a,b; //用來記錄下標,與題無關
struct Node{
double x,y;
int key; //關鍵碼,可有可無,與ab有關
};
Node ar[100005],br[100005];
bool cmpx(Node a,Node b){return a.x<b.x;} //x坐標升序
bool cmpy(Node a,Node b){return a.y<b.y;} //y坐標升序
double min(double a,double b){return a<b?a:b;} //返回最小值
double dis(Node a,Node b){return sqrt(pow(a.x-b.x,2)+pow(a.y-b.y,2));} //返回點與點之間的距離
double cal(int s,int e) //s、e用來表示當前處理的數組中的下標位置
{
int mid,i,j,tail=0; //mid表示數組中間的位置下標 ,tail作為計數變量,是用來br數組存儲標號的
double d; //d表示點對之間的距離
if(s==e) return MAX; //如果只有一個點
mid=(s+e)/2;
d=min(cal(s,mid),cal(mid+1,e)); //遞歸求出左右兩邊的最小距離
//下面是求是否存在左邊的點到右邊某點的距離小於d的點,或者是否存在在右邊的點到左邊某點的距離小於d的點,若是存在,必定處於一個d*2d的矩形中
for(i=mid;i>=s&&ar[mid].x-ar[i].x<d;i--){ //篩選左邊的點,在中間位置左側d以內的點
br[tail++]=ar[i];
}
for(i=mid+1;i<e&&ar[i].x-ar[mid].x<d;i++) //同上,篩選右邊的點
{
br[tail++]=ar[i];
}
sort(br,br+tail,cmpy); //對矩形內的點按照y坐標進行排序
for(i=0;i<tail;i++){ //枚舉矩形內點對之間的距離
for(j=i+1;j<tail&&br[j].y-br[i].y<d;j++){
if(d>dis(br[i],br[j])){ //更新點的值
//a=min(br[i].key,br[j].key);
//b=br[i].key+br[j].key-a;
d=min(d,dis(br[i],br[j]));
}
}
}
return d; //返回最小的點對之間的距離
}
int main()
{
int n;
while(cin>>n&&n){
for(int i=0;i<n;i++){
ar[i].key=i+1; //關鍵碼賦值
scanf("%lf %lf",&ar[i].x,&ar[i].y);
}
sort(ar,ar+n,cmpx); //按x對ar排序
double d=cal(0,n);
printf("%.2lf\n",d/2.0);
}
return 0;
}