平面最近点对(加强版)
题目描述
给定平面上 n n n 个点,找出其中的一对点的距离,使得在这 n n n 个点的所有点对中,该距离为所有点对中最小的
输入格式
第一行: n n n ,保证 2 ≤ n ≤ 200000 2\le n\le 200000 2≤n≤200000 。
接下来 n n n 行:每行两个实数: x y x\ y x y ,表示一个点的行坐标和列坐标,中间用一个空格隔开。
输出格式
仅一行,一个实数,表示最短距离,精确到小数点后面 4 4 4 位。
样例 #1
样例输入 #1
3
1 1
1 2
2 2
样例输出 #1
1.0000
提示
数据保证 0 ≤ x , y ≤ 1 0 9 0\le x,y\le 10^9 0≤x,y≤109
思路
不同于 平面最近点对(加强加强版),之前使用了分治,过于繁琐,而对于这个题来说,可能有更加方便的做法
我们充分发扬人类智慧:
将所有点全部绕原点旋转同一个角度,然后按 x x x 坐标排序
根据数学直觉,在随机旋转后,答案中的两个点在数组中肯定不会离得太远
所以我们只取每个点向后的 5 5 5 个点来计算答案
这样速度快得飞起,在 n = 1000000 n=1000000 n=1000000 时都可以在 1 s 1 s 1s 内卡过
实现上述做法,需要点旋转公式:
x
′
=
(
x
−
x
0
)
∗
c
o
s
(
θ
)
+
(
y
−
y
0
)
∗
s
i
n
(
θ
)
+
x
0
x'= (x - x_0)*cos(\theta) + (y - y_0)*sin(\theta) + x_0
x′=(x−x0)∗cos(θ)+(y−y0)∗sin(θ)+x0
y
′
=
−
(
x
−
x
0
)
∗
s
i
n
(
θ
)
+
(
y
−
y
0
)
∗
c
o
s
(
θ
)
+
x
0
y'=-(x - x_0)*sin(\theta) + (y - y_0)*cos(\theta) + x_0
y′=−(x−x0)∗sin(θ)+(y−y0)∗cos(θ)+x0
若绕原点,则可以代入求解
代码
#include <bits/stdc++.h>
#define int double
#define hp(x,y,a,b) (x-a)*(x-a)+(y-b)*(y-b)
using namespace std;
const long M=1e7;
int a[M],b[M];
struct node{
int x,y;long id;
}p[M];
void calc(int x,int y,int b,long i){
int tx,ty;
tx=x*cos(b)-y*sin(b);
ty=x*sin(b)+y*cos(b);
p[i]=(node){tx,ty,i};
}
int dist=1e19;
int read(){int x;cin>>x;return x;}
long n;
signed main(){
cin>>n;
for(long i=1;i<=n;i++)
calc(a[i]=read(),b[i]=read(),56,i);
sort(p+1,p+1+n,[](node x,node y){return x.x<y.x;});
for(long i=1;i<=n;i++)
for(long j=i+1;j<=min(n,i+2);j++)
dist=min(hp(a[p[i].id],b[p[i].id],a[p[j].id],b[p[j].id]),dist);
printf("%.4lf",sqrt(dist));
return 0;
}