分治 平面上的最近點對

分治 平面上的最近點對
題目描述
給定平面上n個點,找出其中的一對點的距離,使得在這n個點的所有點對中,該距離爲所有點對中最小的。

輸入格式:
多行輸入
第一行:n;2≤n≤200000
接下來n行:每行兩個實數:x y,表示一個點的行座標和列座標,中間用一個空格隔開。

輸出格式:
僅一行,一個實數,表示最短距離,精確到小數點後面4位。

輸入輸出樣例
輸入樣例#1:

10
1 1
1 5
3 1
5 1
5 6
6 7
7 3
8 1
10 3
9 9
3
1 1
1 2
2 2
2
1 1
2 2

輸出樣例#1:

1.0000
1.4142
1.4142

說明
0<=x,y<=10^9

題目分析
先把點座標x從小到大排序,然後從中間分開,最短距離有以下兩種情況:

  1. 最短距離兩點都在同一側:
    對於這種來說遞歸的終止時只需計算2-3點的距離,設計這兩種情況的遞歸終止條件即可。
  2. 最短距離兩點分別在兩側:
    假設已知左邊最短距離爲d1,右邊最短距離爲d2,而這兩部分最短距離d=min(d1,d2),但如果兩點分別在左右半區的情況沒有考慮,現在只需要比距離d小的點,這時我們以中間的座標x0爲準,對[x0-d,x0+d]之間的點進行查找,這時需要查找的點數量大大減少(排除多數點都集中在中間的情況,那就難搞了…),我們就可以用循環來枚舉該區間內所有的點距離,並比較其最短距離是否比之前的d要小。
    在這裏插入圖片描述

代碼詳解

#include<bits/stdc++.h>
using namespace std;
struct node{//定義每個座標
	double x,y;
}a[200010];
int c[200010];
double cmpy(int t1,int t2)  
{  
  return a[t1].y<a[t2].y;  
}  
bool cmp(node t1,node t2)
{
	return t1.x<t2.x;
}
double dis(node t1,node t2)//計算兩點間的距離
{
	return sqrt((t1.x-t2.x)*(t1.x-t2.x)+(t1.y-t2.y)*(t1.y-t2.y));
}
double min(double t1,double t2)//返回較小數值
{
	return t1<t2?t1:t2;
}
double find(int left,int right)
{
	if(left+1==right)//如果只有兩個點,則直接返回兩點距離
		return dis(a[left],a[right]);
	if(left+2==right)//如果只有三個點,則求這三個點相距的最小距離
		return min(dis(a[left],a[right]),min(dis(a[left],a[left+1]),dis(a[left+1],a[right])));
	int mid=(left+right)>>1;//求中點
	double ans=min(find(left,mid),find(mid+1,right));  //將平面一分爲二
    int i,j,cnt=0;  
    //------------------------核心代碼-------------
    //有可能最小值的點位於分界直線兩邊,計算位於分界直線兩邊的點
    for(i=left;i<=right;i++)//將點座標 x 到中間點座標 a[mid] 的距離小於ans的值保存進數組c
		if(abs(a[mid].x-a[i].x)<ans)  
			c[cnt++]=i;  
	sort(c,c+cnt,cmpy);//將點座標按 y 從小到大排序 
    for(i=0;i<cnt;i++) //比較數組c內兩點距離找最小值(暴力,因爲此時數據範圍很小)
		for(j=i+1;j<cnt;j++)  
		{  
            //y座標升序排列,有j>i所以差值肯定爲正,故不需要絕對值
			if(a[c[j]].y-a[c[i]].y>ans)//如果y相差已經大於ans,則算上x肯定大於ans
				break;  
			ans=min(ans,dis(a[c[i]],a[c[j]]));//求出最小值了
		}
    return ans;
}
int main()
{
	int n,i;
    ios::sync_with_stdio(false);
    cin.tie(0);
	while(cin>>n)
	{
		for(i=0;i<n;i++)
			cin>>a[i].x>>a[i].y;
		std::sort(a,a+n,cmp);
		printf("%.4lf\n",find(0,n-1));
	}
	return 0;
}

代碼思路,編輯格式不易,大家覺得還可以可以點贊收藏關注一下吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章