题目描述:
给定平面上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; }