题目: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; }