分治算法五(最近点对---杭电OJ 1007 Quoit Design)

1、问题描述

杭电OJ 1007链接:http://acm.hdu.edu.cn/showproblem.php?pid=1007

即给定坐标系上N个点,找到距离最短的两个点。

2、思路解析

----->如果直接利用两两点比较的话,复杂度太高,为O(n^2),会导致超时

----->简化问题:考虑一维数轴上点的情况,如果对这些点排序O(nlgn),那么最后只需要用O(n)时间就可以找到最小距离,所以总的时间复杂度就是O(nlgn)。

----->回到二维:是不是可以先排序再搜索呢?如果仿照一维的情况,会出现什么问题呢?

=====>>先试着根据 x 轴排序,此时平面上的数据点按照 x 轴已经排好了顺序,那么是不是简单的遍历就能找到最小距离呢?

=====>>二维情况下,距离不是单方面决定的,而是由x,y两个坐标一起决定的,在一个方向上距离最短,不代表最终结果距离最短

=====>>难道这样做不行吗?

=====>>可以顺着这个思路做,但是要修正一下。采用分治的方法。先根据 x 轴对数据点排序,然后从中值点对数组一分为二。分别找到左右两个集合中的最短距离。

然后还有可能存在最短距离的位置就是两个集合之间的区域。对于这个区域,可以把范围限制在【mid - D , mid + D】之间以缩小范围。同时对 y 轴排序,

找出范围在D之内的点,这些点都是可能存在最小距离的区域点。

----->时间复杂度:T(n) = 2*T(n/2) + O(n) = O(nlgn)


/* file: closest							*/
/* 1、find the closet distance oftwo points	*/
/* 2、using divide and conquer algorithm	*/
/* 3、sorting the points by x-coordinate	*/
/* 4、divide the points into two sets by
	  L: x = mid,where mid is the middle of
	  the points's x-coordinate.
	  set S1:on the left of line L
	  set S2:on the right of line L			*/
/* 5、conquer the two sets S1 and S2,
	  and find the minimum distance in set 
	  S1 and S2. D = min(S1,S2)				*/
/* 6、combine:the minimum distance can also
	  be finded between set S1 and S2.
	  the x-coordinate is limited in 
	  [mid-D,mid+D],and then sort the limited
	  points by y-coordinate. therefore, the 
	  y-coordinate is limited in the length 
	  of D									*/

/*	problem:HDU OJ 1007*/

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;

#define MaxNum 100005

/*=====================================
  Point:struct of a point
		PointArray:input points
		PointY	  :y-coordinate sorting
=======================================*/
struct Point
{
	double x,y;
}PointArray[MaxNum],PointY[MaxNum];

/*=====================================
  CmpX:compare of x-coordinate
=======================================*/
bool CmpX(Point a,Point b)
{
	return a.x < b.x;
}

/*=====================================
  CmpY:compare of y-coordinate
=======================================*/
bool CmpY(Point a,Point b)
{
	return a.y < b.y;
}

/*=====================================
  Minimun:return the minimum
=======================================*/
inline double Minimun(double a, double b)
{
	return (a < b) ? a : b;
}

/*=====================================
  Distance:return the distance between point a, b
=======================================*/
inline double Distance(Point a, Point b)
{
	double Px = a.x - b.x;
	double Py = a.y - b.y;
	return sqrt(Px * Px + Py * Py);
}

/*=====================================
  Closest:return the closest distance of the points
=======================================*/
double Closest(int left, int right)
{
	//middle of the array
	int mid = (left + right) >> 1;
	//Minimun distance
	double MinD;
	int i, j, count;
	//case of two points
	if((right - left) == 1) return Distance(PointArray[left], PointArray[right]);
	//case of three points
	else if((right - left) == 2)
	{
		MinD = Minimun(Distance(PointArray[left], PointArray[right]), Distance(PointArray[left + 1], PointArray[right]));
		return Minimun(Distance(PointArray[left], PointArray[left + 1]), MinD);		
	}
	//conquer
	MinD = Minimun(Closest(left, mid), Closest(mid + 1, right));
	//find the points of x-coordinate in [mid - MinD, mid + MinD]
	for(i = left, count = 0; i <= right; i++)
		if(fabs(PointArray[i].x - PointArray[mid].x) <= MinD)
			PointY[count++] = PointArray[i];
	//sorting PointY by y-coordinate
	sort(PointY, PointY + count, CmpY);
	//Attention:==> for 2-D points,must using two-flod (for) to find the every possible point
	for(i = 0; i < count; i++)
		for(j = i + 1; j < count; j++)
		{
			if(fabs(PointY[i].y - PointY[j].y) >= MinD) break;
			MinD = Minimun(MinD, Distance(PointY[i], PointY[j]));
		}
	return MinD;
}

void main()
{
	int N, i;
	double MinD;
	while(cin>>N,N)
	{
		for(i = 0; i < N; i++)
			scanf("%lf%lf",&PointArray[i].x,&PointArray[i].y);
		sort(PointArray, PointArray + N, CmpX);
		MinD = Closest(0,N-1);
		printf("%.2lf\n",MinD/2);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值