题意

  • 给定若干个点,求最近的两个点的距离

思路

  • 分治

  • 对于所有点,总可以按照以下三种情况处理:

    1.只剩下两个点:直接返回两点距离

    2.只剩下三个点:返回三个距离的最小值

    3.剩下的点超过三个,从中点分开,不断递归至情况一/情况二

  • 由此,总能不断地把大区间二分成小区间,并算出区间内最近点对距离dis

  • 此后,考虑将区间合并,找出可能的在跨区间的最小点对:

    1.记录全部距离mid的距离小于等于dis的点

    2.将这些点按纵坐标排序

    3.二重循环遍历所有点,如果两个点的纵坐标差值大于dis,直接跳出循环,进行剪枝

  • 最后将所有答案整合求最小即可得到答案

AC代码

#include <bits/stdc++.h>
#define ll long long 
using namespace std;

typedef struct{
	ll x,y;
}dot;
int b[404040];
dot a[404040];

int cmpx(dot a,dot b){return a.x<b.x;}
int cmpy(int j,int k){return a[j].y<a[k].y;}

ll dis(dot p,dot q){
	return 	(p.x-q.x)*(p.x-q.x)+(p.y-q.y)*(p.y-q.y);
}

ll deal(int l,int r){
	ll rst;
	if(l+1==r)return dis(a[l],a[r]);
	if(l+2==r)return min(min(dis(a[l],a[r]),dis(a[l+1],a[r])),dis(a[l],a[l+1]));
	int mid=(l+r)>>1;
	rst=min(deal(l,mid),deal(mid+1,r));
	int cnt=0;
	for(int i=l;i<=r;i++){
		if(abs(a[i].x-a[mid].x)<=rst){
			b[cnt++]=i;
		}
	}
	sort(b,b+cnt,cmpy);
	for(int i=0;i<cnt-1;i++){
		for(int j=i+1;j<cnt;j++){
			if(a[b[j]].y-a[b[i]].y>=rst)break;
			rst=min(rst,dis(a[b[j]],a[b[i]]));
		}
	}
	return rst;
}
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		int n;
		scanf("%d",&n);
		for(int i=0;i<n;i++){
			scanf("%lld%lld",&a[i].x,&a[i].y);
		}
		sort(a,a+n,cmpx);
		ll ans=deal(0,n-1);
		printf("%lld\n",ans);
	}
}