# 最近點對-分治
## 題序
給平片上相異$N$點$(x_1,y_1),(x_2,y_2),...,(x_N,y_n)$
請計算最近點對的距離的平方
也就是找出$i,j$,滿足$1≤i<j≤N$,且$(x_i-x_j)^2+(y_i-y_j)^2$為最小
輸入說明:
第一行包含一個正整數$N$,代表平面上的點數。
接下來$N$行,每行包含兩整樹$x_i,y_i$代表第$i$個點的座標
$1≤N≤2×10^5$
$-10^9≤x_i,y_i≤10^9$
$(x_i,y_i)≠(x_j,y_j)≠j$,不存在兩點座標相同
輸出說明:
輸出正整數,代表最近點對的距離的平方
範例輸入
4
1 3
7 5
-4 9
2 -5
範例輸出
40
## 題解
這題我是用分治的想法來做,我們可以利用排序找出中點,把點分成左右兩邊
分出兩個子問題,而最近點對只有三種可能
1.左邊區域的最近點對$(dl)$
2.右邊區域的最近點對$(dr)$
3.一個點在左區域,一個點在右區域的最近點對
1、2、3之中的最小值就是答案
1和2的值都會在遞迴的過程中被傳上來,所以我們只剩下3要怎麼求
我們先定義$d=min(dl,dr)$
我們可以知道3的點只可能會出現在中點向左右展開$d$的空間中
並且我們可以將空間中所有點都用點的$y$座標來排序
並且從每個點向上枚舉看可不可以形成最近點對
當枚舉到兩點距離$>d$時就$break$優化複雜度
但這個方法你可能會想,每次枚舉的區域內有沒有可能存在大量的點導致複雜度大增呢
如果我們仔細想一想會發現對每一次枚舉的點$P$來說,這個空間中的點是常數個
我們是從下向上枚舉(從$y$小的開始)
而有可能和$P$點成為最近點對的範圍就只有點向左、右、上擴張$d$,
形成一個$2d×d$的矩形空間
我們將這個空間分成2等分的正方形,在每一個正方形中長為$d$寬為$d$
根據畢氏定理,對角線為$\sqrt2d$,所以我們可以得知每個正方形內最多只有一個點
根據鴿籠原理,若我們放超過2個點(不包含$P$),必會有一個小矩形有兩個點,且這兩點的距離小於$d$
和前的$d$計算矛盾,故可知最多只有2個點(不包含$P$)
## 時間複雜度分析
每一層都需要對y進行排序,時機複雜度$O(nlogn)$,每次遞迴都將$n/2$共$logn$層,時間複雜度$O(logn)$
總時間複雜度$O(nlog^2n)$
## 實作$O(nlog^2n)$
```cpp=
#include<bits/stdc++.h>
#define LL long long int
#define INF 9223372036854775807
#define x first
#define y second
using namespace std;
LL n;
pair<LL,LL> tmp[200001];
vector<pair<LL,LL>> p;
LL dis(pair<LL,LL> a,pair<LL,LL> b){
LL dx = a.x - b.x , dy = a.y - b.y;
return dx * dx + dy * dy;
}
bool cmp(pair<LL,LL> a,pair<LL,LL> b){
return a.y < b.y;
}
#define mid (l + r) / 2
LL divide(LL l,LL r){
if(l == r)return INF;
LL m = (l + r) / 2;
LL ans = min(divide(l,mid),divide(mid+1,r));
LL midval =p[m].x;
LL pp = -1;
for(LL i = l; i <= r;i++){
if((midval - p[i].x) *(midval - p[i].x) <= ans){
tmp[++pp] = p[i];
}
}
sort(tmp,tmp+pp+1,cmp);
for(LL i = 0; i < pp+ 1; i++){
for(LL j = i + 1; j < pp + 1;j++){
ans = min(ans,dis(tmp[i],tmp[j]));
if((tmp[i].y-tmp[j].y)*(tmp[i].y-tmp[j].y) > ans)break;
}
/*下面這兩種也都是對的
for(LL j = i + 1; j < pp + 1;j++){
ans = min(ans,dis(tmp[i],tmp[j]));
if(dis(tmp[i],tmp[j]) > ans)break;
}
*/
/*
for(LL j = i + 1; j < min(i + 3 , pp+1);j++){
ans = min(ans, dis(tmp[i],tmp[j]));
}
*/
}
return ans;
}
#undef mid
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin >> n;
p.assign(n,{0,0});
for(int i = 0; i < n; i++){
cin >> p[i].x;
cin >> p[i].y;
}
sort(p.begin(),p.end());
cout << divide(0,n - 1) <<'\n';
return 0;
}
```
## 題解$O(nlogn)$
點擊連結以繼續觀看
https://hackmd.io/@samson-note/ry5i3l4La
## 題解-隨機算法
點擊連結以繼續觀看
https://hackmd.io/@samson-note/ry5i3l4La