题目:P1492

考虑分治。先对横坐标进行排序,用solve(l,r)表示表示横坐标在(l,r)内的点之间最小距离。我们将区间(l,r)分成两部分,则我们要求的最小距离可以分成三个部分:

1.左边的点之间的最小距离

2.右边的点之间的最小距离

3.左边的点到右边的的点之间的最小距离

先分别递归求求解solve(l,mid)与solve(mid+1,r),这样就解决了1,2两部分。下面来求解第三部分。设由1,2部分得到的最小距离为d,显而易见,我们只需要考虑横坐标在区间(mid-d,mid+d)的点。

我们将这部分点暴力求出,再将这些点按纵坐标排序,直接暴力求出每对点对间的距离。根据主定理,我们有 f(n)=2f(n/2)+O(nlogn)=O(nlog2n) 。

注意到分治的过程其实与归并排序一致,所以我们可以在对纵坐标归并排序时顺便递归求解,复杂度可以下降到O(nlogn)。

O(nlog2n) 代码

 

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll long long
#define Inf 1e10
int read() {
    int s=0,w=1; char ch=getchar();
    while(ch<'0' || ch>'9') { if(ch=='-') w=-1; ch=getchar();}
    while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;
} 
const int N=4e5+10;
int n;
struct node{
	double x,y;
} a[N],b[N];
bool cmp(node p1,node p2) {
	return p1.x<p2.x;
}
bool cmp2(node p1,node p2) {
	return p1.y<p2.y;
}
double ans;
double dist(node p1,node p2) {
	return sqrt((ll)(p1.x-p2.x)*(p1.x-p2.x)+(ll)((p1.y-p2.y)*(p1.y-p2.y)));
}
double solve(int l,int r) {
	if(l==r) return Inf;
	int mid=(l+r)/2;
	double d=min(solve(l,mid),solve(mid+1,r));
	int js=0;
	for(int i=l;i<=r;i++) if(fabs(a[i].x-a[mid].x) < d) b[++js]=a[i];
	sort(b+1,b+js+1,cmp2);
	for(int i=1;i<=js;i++) {
		for(int j=i+1;j<=js;j++) {
			if(b[j].y-b[i].y >= d) break;
		    d=min(dist(b[i],b[j]),d);
		    d=min(dist(b[i],b[j]),d);
		}
	}
	return d;
}
int main() {
	n=read();
	for(int i=1;i<=n;i++) scanf("%lf %lf",&a[i].x,&a[i].y);
	sort(a+1,a+n+1,cmp);
	ans=solve(1,n);
	printf("%.4lf",ans);
	return 0;
}