题目描述:
给定平面上n个点,找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的。
一、暴力n平方做法
既然是暴力做法,那肯定是非常非常暴力的做法,无疑是枚举。枚举i从1~n,j从i+1~n,用距离公式计算出第i个点和第j个点的距离,然后找到最小值即可。放一道模板题吧!
AC代码:
#include<cstdio>
#include<iostream>
#include<cmath>
#define ll long long
using namespace std;
ll n,mn;
ll x[10005],y[10005],now;
double ans;
inline ll dis(ll x1,ll y1,ll x2,ll y2){
return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
}
int main(){
freopen("a.in","r",stdin);
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&x[i],&y[i]);
}
mn=dis(x[1],y[1],x[2],y[2]);
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
now=dis(x[i],y[i],x[j],y[j]);
if(mn>now){
mn=now;
}
}
}
printf("%.4lf",sqrt(mn));
return 0;
}
二、分治nlogn做法
有时候题目的数据范围超过了暴力能接受的范围怎么办呢?这时我们考虑一种分治算法。我们先将所有点按x坐标升序排序一遍。然后考虑分治,对于当前要处理的[l,r]区间内的所有点(若l==r返回INF),我们将其按照横坐标均分为一半对一半,并在他们直接划出一条分界线。左右两边各自算出其包含范围内的最小值,然后怎么合并两边的点呢?我们算出左右两边的最小值分别为d1 , d2(递归处理)。我们再设一个d=min(d1,d2)。很明显,左右两边的点只有到分界线距离在d以内才有机会更新最小值,而且我们还能发现这样的点不超过8个,可以很大程度缩小搜索范围。于是我们就在很短的时间内合并成功了!这种做法也丢一道模板题吧!
AC代码:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
const int Maxn=200005;
struct node{
ll x,y;
}a[Maxn];
int n,tmp[Maxn];ll inf;
inline ll dis(node s1,node s2){
return (s1.x-s2.x)*(s1.x-s2.x)+(s1.y-s2.y)*(s1.y-s2.y);
}
inline ll mn(ll x,ll y){
return x<y?x:y;
}
inline ll ab(ll x){
return x>0?x:-x;
}
bool cmp(node s1,node s2){
if(s1.x!=s2.x)return s1.x<s2.x;
return s1.y<s2.y;
}
ll merge(int l,int r){
ll d=inf;
if(l==r){
return d;
}
if(l+1==r){
return dis(a[l],a[r]);
}
int mid=l+r>>1,k=0;
ll d1=merge(l,mid);
ll d2=merge(mid+1,r),dt;
d=mn(d1,d2);
for(int i=l;i<=r;i++){
dt=a[mid].x-a[i].x;
if(dt*dt<=d){
tmp[++k]=i;
}
}
for(int i=1;i<=k;i++){
for(int j=i+1;j<=k;j++){
d=mn(d,dis(a[tmp[i]],a[tmp[j]]));
}
}
return d;
}
inline ll read(){
ll x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){
x=x*10;x=x+c-'0';c=getchar();
}
return x*f;
}
int main(){
inf=1000000000000000ll;
scanf("%d",&n);
for(int i=1;i<=n;i++){
a[i].x=read();a[i].y=read();
}
sort(a+1,a+1+n,cmp);
printf("%.4lf\n",sqrt(merge(1,n)));
return 0;
}