1. 程式人生 > >分治演算法五(最近點對---杭電OJ 1007 Quoit Design)

分治演算法五(最近點對---杭電OJ 1007 Quoit Design)

1、問題描述

即給定座標系上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);
	}
}