/* 对于这类题目,首先要对进行划分区域,要划分区域就要按照X坐标进行排序,然后进行划分,当划分到只有两个点或者三个点时,在进行求点之间的距离,并记录! 接下来进来区域之间的合并,当当前的点到刚才以这个划分区域为准的中线距离大于刚才我们所求的距离时,则放弃,否则进行更新最短距离! 总体算法思想就是利用递归最后进行合并的思想! */ #include<iostream> #include<cmath> #include<algorithm> #define eps 1e-10 using namespace std; //最近点对问题 const int maxn=200001; const double INF=1e100; typedef struct point { double x,y; int flag; point(){}; point(double xx,double yy){x=xx;y=yy;} }point; point p[maxn]; int n; int com(double x,double y) { if(x==y) return 0; if(x>y) return 1; return -1; } double min(double a, double b) { return a<b?a:b; } bool cmp1(point a,point b)//对于x进行排序 { return com(a.x,b.x)<0; } bool cmp2(int a,int b)//对于y进行排序 { return com(p[a].y,p[b].y)<0; } double dist(point &a,point &b)//传入两个点,求两点距离 { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } int b[maxn], k; double min_dis(point p[],int left,int right)//求从left到right这些点p[i]的最近点对 { int mid=(left+right)>>1,i,j; double ret=INF; if(left>=right) return ret; for(i=mid; i>=left && !com(p[i].x,p[mid].x); i--);//取中间的点 ret=min_dis(p,left,i); for(i=mid; i<=right && !com(p[i].x,p[mid].x); i++); ret=min(ret,min_dis(p,i,right)); k = 0;//寻找那些到我们所参考的点比ret小的点的个数 for(i=mid; i>=left && com(p[mid].x-p[i].x,ret)<=0; i--) b[++k]=i; for(i=mid+1; i<=right && com(p[i].x-p[mid].x,ret)<=0; i++) b[++k]=i; sort(b+1,b+k+1,cmp2); for(i=1; i<=k; i++) for(j=1; j+i<=k; j++) if(i+j<=k && com(p[b[i+j]].y-p[b[i]].y,ret) < 0 && p[b[i]].flag != p[b[i+j]].flag) ret=min(ret,dist(p[b[i]],p[b[i+j]])); return ret; } int main() { // freopen("in.txt","r",stdin); int i,j,k,test; scanf("%d",&test); while(test--) { scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%lf%lf",&p[i].x,&p[i].y); p[i].flag=1; } for(i=1; i<=n; i++) { scanf("%lf %lf", &p[i+n].x, &p[i+n].y); p[i+n].flag = 2; } n<<=1; sort(p+1,p+n+1,cmp1); printf("%.3lf/n",min_dis(p,1,n)); } return 0; } 对于这类题目,首先要对进行划分区域,要划分区域就要按照X坐标进行排序,然后进行划分,当划分到只有两个点或者三个点时,在进行求点之间的距离,并记录!
接下来进来区域之间的合并,当当前的点到刚才以这个划分区域为准的中线距离大于刚才我们所求的距离时,则放弃,否则进行更新最短距离!
总体算法思想就是利用递归最后进行合并的思想!
算法模版如下:
/* 对于这类题目,首先要对进行划分区域,要划分区域就要按照X坐标进行排序,然后进行划分,当划分到只有两个点或者三个点时,在进行求点之间的距离,并记录! 接下来进来区域之间的合并,当当前的点到刚才以这个划分区域为准的中线距离大于刚才我们所求的距离时,则放弃,否则进行更新最短距离! 总体算法思想就是利用递归最后进行合并的思想! */ #include<iostream> #include<cmath> #include<algorithm> #define eps 1e-10 using namespace std; //最近点对问题 const int maxn=200001; const double INF=1e100; typedef struct point { double x,y; point(){}; point(double xx,double yy){x=xx;y=yy;} }point; point p[maxn]; int n; int com(double x,double y) { if(x==y) return 0; if(x>y) return 1; return -1; } double min(double a, double b) { return a<b?a:b; } bool cmp1(point a,point b)//对于x进行排序 { return com(a.x,b.x)<0; } bool cmp2(int a,int b)//对于y进行排序 { return com(p[a].y,p[b].y)<0; } double dist(point &a,point &b)//传入两个点,求两点距离 { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } int b[maxn], k; double min_dis(point p[],int left,int right)//求从left到right这些点p[i]的最近点对 { int mid=(left+right)>>1,i,j; double ret=INF; if(left>=right) return ret; for(i=mid; i>=left && !com(p[i].x,p[mid].x); i--);//取中间的点 ret=min_dis(p,left,i); for(i=mid; i<=right && !com(p[i].x,p[mid].x); i++); ret=min(ret,min_dis(p,i,right)); k = 0;//寻找那些到我们所参考的点比ret小的点的个数 for(i=mid; i>=left && com(p[mid].x-p[i].x,ret)<=0; i--) b[++k]=i; for(i=mid+1; i<=right && com(p[i].x-p[mid].x,ret)<=0; i++) b[++k]=i; sort(b+1,b+k+1,cmp2); for(i=1; i<=k; i++) for(j=1; j+i<=k; j++) if(i+j<=k && com(p[b[i+j]].y-p[b[i]].y,ret) < 0) ret=min(ret,dist(p[b[i]],p[b[i+j]])); return ret; } int main() { freopen("in.txt","r",stdin); int i,j,k,test; scanf("%d",&test); while(test--) { scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%lf%lf",&p[i].x,&p[i].y); // p[i].flag=1; } sort(p+1,p+n+1,cmp1); printf("%.3lf/n",min_dis(p,1,n)); } return 0; }
http://poj.org/problem?id=3714