平面最近点对
暴力的话,时间复杂度是
O
(
n
2
)
O(n^2)
O(n2)
所以我们考虑分治
首先按
x
x
x排序
接着以中间,分成两个区域
分别计算最近点对
然后取两者较小的值
d
d
d
接着考虑一个在左边一个字右边的点对
A
A
A点的最近点对,只会在右边这个
d
×
2
d
d \times 2d
d×2d的矩形区域里
因为向上
d
d
d,向下
d
d
d,向右
d
d
d
(当然你要说只会在以
A
A
A为圆心,
d
d
d为半径的圆里也可以,只是矩形好算)
接着证明这个矩形里,最多只有6个点
把这个矩形分成如下的6个小矩形
假设超过了6个点,根据抽屉原理,必然有一个小矩形有2个点
但是每个小矩形的对角线的距离
(
d
2
)
2
+
(
2
d
3
)
2
=
5
d
6
<
d
\sqrt{(\frac{d}{2})^2 + (\frac{2d}{3})^2} =\frac{5d}{6}<d
(2d)2+(32d)2=65d<d
与右边区域的最近点对距离为
d
d
d矛盾
所以只有6个点
那么时间复杂度为
排序
O
(
n
log
2
n
)
O(n\log_2 n)
O(nlog2n)
分治
T
(
n
)
=
2
T
(
n
2
)
+
6
n
=
2
T
(
n
2
)
+
O
(
n
)
T(n)=2T(\frac{n}{2})+6n=2T(\frac{n}{2})+O(n)
T(n)=2T(2n)+6n=2T(2n)+O(n)
由主定理
T
(
n
)
=
O
(
n
log
2
n
)
T(n)=O(n\log_2 n)
T(n)=O(nlog2n)
所以总的复杂度是
O
(
n
log
2
n
)
O(n\log_2 n)
O(nlog2n)
OJ
洛谷P1257 ,P1429
做法其实大致和上面将的差不多
只是找一个点在左边一个点在右边的点对的时候,
是直接将
m
i
d
mid
mid为中心左边
d
d
d,右边
d
d
d的点找出来,排个序,然后比。
总的时间复杂度其实差不多
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef pair<double,double> P;
const int N=200005;
P p[N];//坐标
int n,temp[N];
bool cmpX(const P& a,const P& b){
return a.first<b.first;
}
bool cmpY(int a,int b){
if(p[a].second!=p[b].second)return p[a].second<p[b].second;
return p[a].first<p[b].first;
}
double get_distance(const P& a,const P& b){
return sqrt((a.first-b.first)*(a.first-b.first)+(a.second-b.second)*(a.second-b.second));
}
double solve(int left,int right){
double d=2e9;
if(left==right)return d;
else if(left+1==right)return get_distance(p[left],p[right]);
int mid=(left+right)>>1;
d=min(solve(left,mid),solve(mid+1,right));
int t=0;
//找以mid为中心左右距离d的
for(int i=left;i<=right;++i){
//注意这里temp是复制下标,而不是坐标,复制坐标容易T(因为我就T了
if(fabs(p[i].first-p[mid].first)<d)
temp[t++]=i;
}
sort(temp,temp+t,cmpY);
for(int i=0;i<t-1;++i){
for(int j=i+1;j<t&&p[temp[j]].second-p[temp[i]].second<d;++j){
d=min(d, get_distance(p[temp[i]],p[temp[j]]));
}
}
return d;
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;++i){
double x,y;
scanf("%lf%lf",&x,&y);
p[i]=make_pair(x,y);
}
sort(p,p+n,cmpX);
printf("%.4lf\n",solve(0,n-1));
return 0;
}
洛谷P7883
这次要输出距离平方
可以用之前的方法,然后最后输出平方
值得注意的是,这里是四舍五入的,也可以用printf("%.0lf\n",ans*ans)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef pair<int,int> P;
const int N=400005;
P p[N];//坐标
int n,temp[N];
bool cmpX(const P& a,const P& b){
return a.first<b.first;
}
bool cmpY(int a,int b){
if(p[a].second!=p[b].second)return p[a].second<p[b].second;
return p[a].first<p[b].first;
}
double get_distance(const P& a,const P& b){
return sqrt(1ll*(a.first-b.first)*(a.first-b.first)+1ll*(a.second-b.second)*(a.second-b.second));
}
double solve(int left,int right){
double d=2e9;
if(left==right)return d;
else if(left+1==right)return get_distance(p[left],p[right]);
int mid=(left+right)>>1;
d=min(solve(left,mid),solve(mid+1,right));
int t=0;
//找以mid为中心左右距离d的
for(int i=left;i<=right;++i){
//注意这里temp是复制下标,而不是坐标,复制坐标容易T(因为我就T了
if(fabs(p[i].first-p[mid].first)<d)
temp[t++]=i;
}
sort(temp,temp+t,cmpY);
for(int i=0;i<t-1;++i){
for(int j=i+1;j<t&&p[temp[j]].second-p[temp[i]].second<d;++j){
d=min(d, get_distance(p[temp[i]],p[temp[j]]));
}
}
return d;
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;++i){
int x,y;
scanf("%d%d",&x,&y);
p[i]=make_pair(x,y);
}
sort(p,p+n,cmpX);
double ans=solve(0,n-1);
printf("%lld\n",llround(ans*ans));
return 0;
}
poj3714
差不多,就是算不同种类的最近点对
我们定义同类的距离无穷大就可以了
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=200005;
class P{
public:
double x;
double y;
int f;
};
P p[N];
const double inf=5e10;
int temp[N],n;
bool cmpX(const P& a,const P& b){
return a.x<b.x;
}
bool cmpY(const int& a,const int& b){
return p[a].y<p[b].y;
}
double get_distance(const P& a,const P& b){
if(a.f==b.f)return inf;
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double solve(int l,int r){
if(l>=r)return inf;
if(l+1==r)return get_distance(p[l],p[r]);
int mid=(l+r)/2;
double d=solve(l,mid);
double t=solve(mid+1,r);
if(t<d)d=t;
int cnt=0;
for(int i=l;i<=r;++i){
if(fabs(p[i].x-p[mid].x)<d){
temp[cnt++]=i;
}
}
sort(temp,temp+cnt,cmpY);
for(int i=0;i+1<cnt;++i){
for(int j=i+1;j<cnt;++j){
if(p[j].y-p[i].y>=d)break;
t=get_distance(p[i],p[j]);
if(t<d)d=t;
}
}
return d;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=0;i<n;++i){
scanf("%lf%lf",&p[i].x,&p[i].y);
p[i].f=0;
}
for(int i=n;i<2*n;++i){
scanf("%lf%lf",&p[i].x,&p[i].y);
p[i].f=1;
}
sort(p,p+2*n,cmpX);
printf("%.3lf\n",solve(0,2*n-1));
}
return 0;
}