分治算法解决最短距离问题

分治算法解决最短距离

题目:题目描述
给定平面上n个点,找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的
输入格式
第一行:n;2≤n≤200000
接下来n行:每行两个实数:x y,表示一个点的行坐标和列坐标,中间用一个空格隔开。
输出格式
仅一行,一个实数,表示最短距离,精确到小数点后面4位。
输入输出样例
输入 #1
3
1 1
1 2
2 2
输出 #1
1.0000
以上是某次信息竞赛试题,本次作业的要求是:

1)应用分治算法实现上述功能;

2)应用改进的分治算法实现上述功能;

3)应用分治算法实现上述功能外,还具备输出最短距离两点坐标的功能;

4)应用改进的分治算法实现上述功能外,还具备输出最短距离两点坐标的功能。

答题要求:首先回答程序完成的是上述4个中的哪一个,然后是运行截图,最后是源代码(仅限C或C++实现)。所谓改进的分治算法,就是在对中间分界线附近点的、根据y坐标排序的时间复杂度是以O(n)还是以O(nlogn)实现。

##分析
算法设计:

(1)蛮力法:计算出所有点对的距离,由此找到距离最小的点对。本题暂不考虑。

(2)分治法:将平面上的点集S按x坐标排序后,若只有1个点,则返回;若有两个点,则直接计算两点距离为最近距离;若有三个点,则两两计算出距离,得到最近距离。

若大于三个点,则将其线性分割成大小大致相等的2个子集S1,S2。最近点对只会出现在以下三种情况中:①点对在子集S1中;②点对在子集S2中;③一个点在S1中,另一个点在S3中。

因此,递归地在S1,S2上解最近点对问题,分别得到S1,S2中的最小距离d1,d2,取d1,d2中的最小距离d和点对。再找出跨越S1,S2的最近点对:合并S1,S2找到点集中x坐标在区间[mid-d,mid+d]范围内的所有点。按y坐标不减排序,循环每个点,找它后面7个点的最小距离,与d比较,比d小则更新d。最后即求得最近点对距离。

算法分析复杂度:

(1)蛮力法:n个点之间两两求距离的事件复杂度是O(n²)。

(2)分治法:在计算过程中,每次获得最短距离时,要记录下点对的位置信息。分治法的递归操作每次都是均等分,所以其递归树为logn层,递归函数中的操作均为线性,所以总的时间复杂度为O(nlogn)。

T(n) = 2T(n/2)+O(n)递推= O(nlogn)

以下是代码

1#include
#include
#include
#include
#include
#include
using namespace std;
 const double INF = 1e20;  
const int N = 200005;  
  
struct point
{//点结构 
	double x;//x坐标 
	double y;//y坐标 
};
 
double Distance(point a,point b)
{//计算两点间距离 
	double s;
	s=sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
	return s;
}
bool cmp1(point a,point b)
{//按y排序辅助函数 
	return a.y<b.y;
}
bool cmp2(point a,point b)
{//按x排序辅助函数 
	return a.x<b.x;
}
 
double ClosestPoint(point s[],int low,int high)
{
	double d1,d2,d3,d;
	int mid,i,j,index;
	point P[high-low+1]; 
	if(high-low==1)
	{//只有两个点的时候 
		return Distance(s[low],s[high]); 
	} 
	if(high-low==2)
	{//当有三个点的时候
		//两两计算,找出距离最近的两点 
		d1=Distance(s[low],s[low+1]);
		d2=Distance(s[low+1],s[high]);
		d3=Distance(s[low],s[high]);
		if((d1<d2)&&(d1<d3))
		{
			return d1;
		} 
		else if (d2<d3)
		{
		 
			return d2;
		}
		else
		{
			return d3;
		}	
	}
	//三个结点以上的采用递归的办法
	
	mid=(low+high)/2;
	d1=ClosestPoint(s,low,mid);//左递归 
	d2=ClosestPoint(s,mid+1,high);//右递归 
	if(d1<d2)
	{//比较左右递归所得的最近点对距离 
		d=d1;
	 } 
	else
	{
		d=d2;
	}
	
	index=0;
	for(i=mid;(i>=low)&&((s[mid].x-s[i].x)<d);i--)
	{//记录[mid-d,mid]区域的点 
		P[index++]=s[i];
	}
	for(i=mid+1;(i<=high)&&((s[i].x-s[mid].x)<d);i++)
	{//记录[mid,mid+d]区域的点 
		P[index++]=s[i];
	}
	sort(P,P+index,cmp1);//对给定区间所有元素进行排序
	for(i=0;i<index;i++)
	{//找出[mid-d,mid+d]中的最近点对
		for(j=i+1;j<index&&j<index;j++)//对y坐标排序时间复杂度为O(n) 
		{
			if((P[j].y-P[i].y)>=d)
			{
				break;
			}
			else
			{
				d3=Distance(P[i],P[j]);
				if(d3<d)
				{
					d=d3;
				}
			}
		 } 
		 //return d;	
	 }
	 return d;
}
 
 
int main()
{
	point p[100];
	int n;
	double minDist;
  while(true)  
    {  
        scanf("%d",&n);  
        if(n==0)  
            break;  
        for(int i = 0; i < n; i++)  
            scanf("%lf %lf",&p[i].x,&p[i].y);  
       	sort(p,p+n,cmp2);//对所有点在x轴上排序 
        printf("%.4lf\n",ClosestPoint(p,0,n-1));
    }  
    return 0;  

}2#include
#include
#include
#include
#include
#include
using namespace std;
 const double INF = 1e20;  
const int N = 200005;  
  
struct point
{//点结构 
	double x;//x坐标 
	double y;//y坐标 
};
 
double Distance(point a,point b)
{//计算两点间距离 
	double s;
	s=sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
	return s;
}
bool cmp1(point a,point b)
{//按y排序辅助函数 
	return a.y<b.y;
}
bool cmp2(point a,point b)
{//按x排序辅助函数 
	return a.x<b.x;
}
 
double ClosestPoint(point s[],int low,int high)
{
	double d1,d2,d3,d;
	int mid,i,j,index;
	point P[high-low+1]; 
	if(high-low==1)
	{//只有两个点的时候 
		return Distance(s[low],s[high]); 
	} 
	if(high-low==2)
	{//当有三个点的时候
		//两两计算,找出距离最近的两点 
		d1=Distance(s[low],s[low+1]);
		d2=Distance(s[low+1],s[high]);
		d3=Distance(s[low],s[high]);
		if((d1<d2)&&(d1<d3))
		{
			return d1;
		} 
		else if (d2<d3)
		{
		 
			return d2;
		}
		else
		{
			return d3;
		}	
	}
	//三个结点以上的采用递归的办法
	
	mid=(low+high)/2;
	d1=ClosestPoint(s,low,mid);//左递归 
	d2=ClosestPoint(s,mid+1,high);//右递归 
	if(d1<d2)
	{//比较左右递归所得的最近点对距离 
		d=d1;
	 } 
	else
	{
		d=d2;
	}
	
	index=0;
	for(i=mid;(i>=low)&&((s[mid].x-s[i].x)<d);i--)
	{//记录[mid-d,mid]区域的点 
		P[index++]=s[i];
	}
	for(i=mid+1;(i<=high)&&((s[i].x-s[mid].x)<d);i++)
	{//记录[mid,mid+d]区域的点 
		P[index++]=s[i];
	}
	sort(P,P+index,cmp1);//对给定区间所有元素进行排序
	for(i=0;i<index;i++)
	{//找出[mid-d,mid+d]中的最近点对
		for(j=i+1;j<i+6&&j<index;j++)//对y坐标排序时间复杂度为O(nlogn)
		{
			if((P[j].y-P[i].y)>=d)
			{
				break;
			}
			else
			{
				d3=Distance(P[i],P[j]);
				if(d3<d)
				{
					d=d3;
				}
			}
		 } 
		 //return d;	
	 }
	 return d;
}
 
 
int main()
{
	point p[100];
	int n;
	double minDist;
  while(true)  
    {  
        scanf("%d",&n);  
        if(n==0)  
            break;  
        for(int i = 0; i < n; i++)  
            scanf("%lf %lf",&p[i].x,&p[i].y);  
       	sort(p,p+n,cmp2);//对所有点在x轴上排序 
        printf("%.4lf\n",ClosestPoint(p,0,n-1));
    }  
    return 0;  

}3#include
#include
#include
#include
#include
#include
using namespace std;
 const double INF = 1e20;  
const int N = 200005;  
  
struct point
{//点结构 
	double x;//x坐标 
	double y;//y坐标 
};
 
double Distance(point a,point b)
{//计算两点间距离 
	double s;
	s=sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
	return s;
}
bool cmp1(point a,point b)
{//按y排序辅助函数 
	return a.y<b.y;
}
bool cmp2(point a,point b)
{//按x排序辅助函数 
	return a.x<b.x;
}
 
double ClosestPoint(point s[],int low,int high,point rec[])
{
	double d1,d2,d3,d;
	int mid,i,j,index;
	double x1,y1,x2,y2;//记录最近点的位置
	point P[high-low+1],temp1[2],temp2[2],temp3[2];//temp1记录左边的最近点对,temp2记录右边,temp3记录中间 
	if(high-low==1)
	{//只有两个点的时候 
		rec[0].x=s[low].x;
		rec[0].y=s[low].y;
		rec[1].x=s[high].x;
		rec[1].y=s[high].y;
		return Distance(s[low],s[high]); 
	} 
	if(high-low==2)
	{//当有三个点的时候
		//两两计算,找出距离最近的两点 
		d1=Distance(s[low],s[low+1]);
		d2=Distance(s[low+1],s[high]);
		d3=Distance(s[low],s[high]);
		if((d1<d2)&&(d1<d3))
		{
			rec[0].x=s[low].x;
			rec[0].y=s[low].y;
			rec[1].x=s[low+1].x;
			rec[1].y=s[low+1].y;
			return d1;
		} 
		else if (d2<d3)
		{
		 	rec[0].x=s[low+1].x;
			rec[0].y=s[low+1].y;
			rec[1].x=s[high].x;
			rec[1].y=s[high].y;	
			return d2;
		}
		else
		{
		 	rec[0].x=s[low].x;
			rec[0].y=s[low].y;
			rec[1].x=s[high].x;
			rec[1].y=s[high].y;
			return d3;
		}	
	}
	//三个结点以上的采用递归的办法
	
	mid=(low+high)/2;
	d1=ClosestPoint(s,low,mid,rec);//左递归 
	temp1[0]=rec[0];
	temp1[1]=rec[1];
	d2=ClosestPoint(s,mid+1,high,rec);//右递归 
	temp2[0]=rec[0];
	temp2[1]=rec[1];
	if(d1<d2)
	{//比较左右递归所得的最近点对距离 
		d=d1;
		rec[0]=temp1[0];
		rec[1]=temp1[1];
	 } 
	else
	{
		d=d2;
		rec[0]=temp2[0];
		rec[1]=temp2[1];
	}
	
	index=0;
	for(i=mid;(i>=low)&&((s[mid].x-s[i].x)<d);i--)
	{//记录[mid-d,mid]区域的点 
		P[index++]=s[i];
	}
	for(i=mid+1;(i<=high)&&((s[i].x-s[mid].x)<d);i++)
	{//记录[mid,mid+d]区域的点 
		P[index++]=s[i];
	}
	sort(P,P+index,cmp1);//对给定区间所有元素进行排序
	for(i=0;i<index;i++)
	{//找出[mid-d,mid+d]中的最近点对
		for(j=i+1;j<index&&j<index;j++)//对y坐标排序时间复杂度为O(n) 
		{
			if((P[j].y-P[i].y)>=d)
			{
				break;
			}
			else
			{
				d3=Distance(P[i],P[j]);
				if(d3<d)
				{
					rec[0].x=P[i].x;
					rec[0].y=P[i].y;
                    rec[1].x=P[j].x;
					rec[1].y=P[j].y;	
					d=d3;
				}
			}
		 } 
		 //return d;	
	 }
	 return d;
}
int main()
{
	point p[100];
	int n;
	double minDist;
  while(true)  
    {  
        scanf("%d",&n);  
        if(n==0)  
            break;  
        for(int i = 0; i < n; i++)  
            scanf("%lf %lf",&p[i].x,&p[i].y);  
       	sort(p,p+n,cmp2);//对所有点在x轴上排序 
    point index[2];
    printf("%.4lf\n",ClosestPoint(p,0,n-1,index));
	printf("最短距离的两点坐标分别为:") ;
	printf("\n");
	printf("%.4lf	%2.4lf",index[0].x,index[0].y) ;
	printf("\n");
	printf("%.4lf	%2.4lf",index[1].x,index[1].y) ;
    } 
    return 0;
}4#include
#include
#include
#include
#include
#include
using namespace std;
 const double INF = 1e20;  
const int N = 200005;  
  
struct point
{//点结构 
	double x;//x坐标 
	double y;//y坐标 
};
 
double Distance(point a,point b)
{//计算两点间距离 
	double s;
	s=sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
	return s;
}
bool cmp1(point a,point b)
{//按y排序辅助函数 
	return a.y<b.y;
}
bool cmp2(point a,point b)
{//按x排序辅助函数 
	return a.x<b.x;
}
 
double ClosestPoint(point s[],int low,int high,point rec[])
{
	double d1,d2,d3,d;
	int mid,i,j,index;
	double x1,y1,x2,y2;//记录最近点的位置
	point P[high-low+1],temp1[2],temp2[2],temp3[2];//temp1记录左边的最近点对,temp2记录右边,temp3记录中间 
	if(high-low==1)
	{//只有两个点的时候 
		rec[0].x=s[low].x;
		rec[0].y=s[low].y;
		rec[1].x=s[high].x;
		rec[1].y=s[high].y;
		return Distance(s[low],s[high]); 
	} 
	if(high-low==2)
	{//当有三个点的时候
		//两两计算,找出距离最近的两点 
		d1=Distance(s[low],s[low+1]);
		d2=Distance(s[low+1],s[high]);
		d3=Distance(s[low],s[high]);
		if((d1<d2)&&(d1<d3))
		{
			rec[0].x=s[low].x;
			rec[0].y=s[low].y;
			rec[1].x=s[low+1].x;
			rec[1].y=s[low+1].y;
			return d1;
		} 
		else if (d2<d3)
		{
		 	rec[0].x=s[low+1].x;
			rec[0].y=s[low+1].y;
			rec[1].x=s[high].x;
			rec[1].y=s[high].y;	
			return d2;
		}
		else
		{
		 	rec[0].x=s[low].x;
			rec[0].y=s[low].y;
			rec[1].x=s[high].x;
			rec[1].y=s[high].y;
			return d3;
		}	
	}
	//三个结点以上的采用递归的办法
	
	mid=(low+high)/2;
	d1=ClosestPoint(s,low,mid,rec);//左递归 
	temp1[0]=rec[0];
	temp1[1]=rec[1];
	d2=ClosestPoint(s,mid+1,high,rec);//右递归 
	temp2[0]=rec[0];
	temp2[1]=rec[1];
	if(d1<d2)
	{//比较左右递归所得的最近点对距离 
		d=d1;
		rec[0]=temp1[0];
		rec[1]=temp1[1];
	 } 
	else
	{
		d=d2;
		rec[0]=temp2[0];
		rec[1]=temp2[1];
	}
	
	index=0;
	for(i=mid;(i>=low)&&((s[mid].x-s[i].x)<d);i--)
	{//记录[mid-d,mid]区域的点 
		P[index++]=s[i];
	}
	for(i=mid+1;(i<=high)&&((s[i].x-s[mid].x)<d);i++)
	{//记录[mid,mid+d]区域的点 
		P[index++]=s[i];
	}
	sort(P,P+index,cmp1);//对给定区间所有元素进行排序
	for(i=0;i<index;i++)
	{//找出[mid-d,mid+d]中的最近点对
		for(j=i+1;j<i+6&&j<index;j++)//对y坐标排序时间复杂度为O(nlogn)
		{
			if((P[j].y-P[i].y)>=d)
			{
				break;
			}
			else
			{
				d3=Distance(P[i],P[j]);
				if(d3<d)
				{
					rec[0].x=P[i].x;
					rec[0].y=P[i].y;
                    rec[1].x=P[j].x;
					rec[1].y=P[j].y;	
					d=d3;
				}
			}
		 } 
		 //return d;	
	 }
	 return d;
}
 
 
int main()
{
	point p[100];
	int n;
	double minDist;
  while(true)  
    {  
        scanf("%d",&n);  
        if(n==0)  
            break;  
        for(int i = 0; i < n; i++)  
            scanf("%lf %lf",&p[i].x,&p[i].y);  
       	sort(p,p+n,cmp2);//对所有点在x轴上排序 
    point index[2];
    printf("%.4lf\n",ClosestPoint(p,0,n-1,index));
	printf("最短距离的两点坐标分别为:") ;
	printf("\n");
	printf("%.4lf	%2.4lf",index[0].x,index[0].y) ;
	printf("\n");
	printf("%.4lf	%2.4lf",index[1].x,index[1].y) ;
    } 
    return 0;
}

你可能感兴趣的:(分治算法解决最短距离问题)