UVA_10245
这个题目又是对我传统思维的一个撞击,一开始是怎么也想象不到会存在N^2的复杂度还小的算法,后来看了别人的博客之后才发现原来这个题目要用分治的思想。
首先我们把坐标按x升序进行排列,然后定义L、R分别为区间的左右端点(L、R均代表点的标号),mid为区间中点,我们可以先分别暴力求出在[L,mid]、[mid,R]中最短的线段,不妨设其为min,当然最短线段还可能是两个点分别在两个区间之中,但如果存在这样的最短线段,那么线段的两个端点一定会在区间[a,b]中,并且x[mid]-x[a]>=min,x[b]-x[mid]>=min,因为两点之间的距离大于等于两点横坐标差的绝对值。
这样我们便可以写一个迭代函数来实现这一过程。迭代函数中首先要不断二分区间,然后分别去求区间内的最短线段的值并合并,最后以mid为中心,分别向左向右依次扫描可能是最短的跨区间的线段即可,如果长度确实比min小,更新min即可。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
double x[10010],y[10010];
int r[10010];
int cmp(const void *_p,const void *_q)
{
int *p=(int *)_p;
int *q=(int *)_q;
return x[*p]>x[*q]?1:-1;
}
double dis(int i,int j)
{
return sqrt((x[j]-x[i])*(x[j]-x[i])+(y[j]-y[i])*(y[j]-y[i]));
}
double min(double p,double q)
{
return p<q?p:q;
}
double f(int L,int R)
{
int i,j,k,mid;
double ans,temp;
if(L==R)
return 1000000000.0;
if(L==R-1)
return dis(r[L],r[R]);
mid=(L+R)/2;
ans=min(f(L,mid),f(mid,R));
for(i=mid-1;i>=L&&x[r[mid]]-x[r[i]]<ans;i--)
for(j=mid+1;j<=R&&x[r[j]]-x[r[mid]]<ans;j++)
{
temp=dis(r[i],r[j]);
if(temp<ans)
ans=temp;
}
return ans;
}
int main()
{
int i,j,k,N;
double ans;
while(1)
{
scanf("%d",&N);
if(N==0)
break;
for(i=0;i<N;i++)
{
scanf("%lf%lf",&x[i],&y[i]);
r[i]=i;
}
qsort(r,N,sizeof(r[0]),cmp);
ans=f(0,N-1);
if(ans<10000.0)
printf("%.4f\n",ans);
else
printf("INFINITY\n");
}
return 0;
}