第一次见这种问题直接懵圈。。。没想到分治法这么强大,借鉴了lyd的代码:
代码如下
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<iostream> using namespace std; const int maxn=200010; struct point{ int x,y,z; }; point a[maxn],b[maxn]; int n,m,t,i; double ans=0; bool cmp(point a1,point a2){ return a1.x<a2.x; } double dist(point a1,point a2){ return sqrt(1.0*(a1.x-a2.x)*(a1.x-a2.x)+1.0*(a1.y-a2.y)*(a1.y-a2.y)); } void merge(int l,int mid,int r){//按照y坐标排序,分治法 int i=l,j=mid+1,k; for(k=l;k<=r;k++){//两个部分已经按y坐标排好序,直接合并 if(j>r||i<=mid&&a[i].y<a[j].y)b[k]=a[i++]; else b[k]=a[j++]; } for(k=l;k<=r;k++)a[k]=b[k]; } void solve(int l,int r){ if(l==r)return;//分治边界 int mid=(l+r)>>1,p=0,x=a[mid].x,i,j; solve(l,mid);//解决左半部分 solve(mid+1,r);//右半部分 merge(l,mid,r);//合并 for(i=l;i<=r;i++){//排除不肯能选项 if(abs(a[i].x-x)<=ans)b[++p]=a[i]; } for(i=1;i<p;i++) for(j=i+1;j<=p;j++){ if(b[j].y-b[i].y<ans){//y坐标之差小于ans才有可能是 if(b[i].z!=b[j].z)//如果不在一个集合 ans=min(ans,dist(b[i],b[j])); } else//若这个不行,后面的肯定也不行 break; } } int main(){ cin>>t; while(t--){ cin>>n; for(i=1;i<=n;i++)scanf("%d%d",&a[i].x,&a[i].y),a[i].z=1; for(i=n+1,n*=2;i<=n;i++)scanf("%d%d",&a[i].x,&a[i].y),a[i].z=2; ans=dist(a[1],a[n]); sort(a+1,a+1+n,cmp);//根据x坐标排序 solve(1,n); printf("%.3f\n",ans); } }