题目描述

给定平面上n个点,找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的
输入格式

第一行:n;2≤n≤200000

接下来n行:每行两个实数:x y,表示一个点的行坐标和列坐标,中间用一个空格隔开。
输出格式

仅一行,一个实数,表示最短距离,精确到小数点后面4位。
输入输出样例
输入 #1

3
1 1
1 2
2 2

输出 #1

1.0000

平面上最近点对分治算法 算法导论

如图(从360doc拷过来的图片)

原图链接:http://www.360doc.com/content/19/0303/18/32937624_818850914.shtml

步骤如下:

  • 递归出口是 l e f t = = r i g h t left==right​ left==right l e f t > r i g h t left>right​ left>right

  • 每次递归可以得到

    • 两个点都在左边最小距离 D i s t L e f DistLef​ DistLef

    • 两个点都在右边最小距离 D i s t R i g DistRig​ DistRig

    • 一个点在左边,一个点在右边的最小距离 D i s t M i d DistMid DistMid

    • 3 3 3者取最小返回

    • return min(DistLef, DistRig, DistMid)
      
  • D i s t M i d DistMid DistMid一定在图中两条绿线中间,并且不会超过16个点

  • 中间的排序可以用归并O(n)统计出来

代码如下

//初始化要记得排好序
bool cmpx(Node& A, Node& B) { //排序,先x增后y增
  return Ax==Bx ? Ay < By : Ax < Bx;
}
//算法导论里的板子
#define DINF (1e18)
double closep(int lef, int rig) {
	if(lef == rig) return DINF;
	if(lef+1 == rig) return dist(a[lef], a[rig]);
	//
	int mid = (lef + rig) >> 1;
	Node midp = a[mid];
	double lmin = closep(lef, mid);
	double rmin = closep(mid+1, rig);
	//
	double tmin = min(lmin, rmin);
	int sz = 0, i = lef, j = mid+1;
	//按y轴递增归并排序 想偷懒可以拷到T[]直接sort()
	while(i<=mid && j<=rig) {
		if(a[i].y < a[j].y) T[sz++] = a[i++];
		else T[sz++] = a[j++];
	}
	while(i <= mid) T[sz++] = a[i++];
	while(j <= rig) T[sz++] = a[j++];
	//
	for(i=rig; i>=lef; i--) a[i] = T[--sz];
	sz = 0;
	for(i=lef; i<=rig; i++) //中间线[-tmin, +tmin]范围内的点加入T数组
		if(fabs(a[i].x-midp.x) < tmin) T[sz++] = a[i];
	for(i=0; i<sz; i++) //暴力这个复制出来的T[]数组
		for(j=i+1; j<sz && (T[j].x-T[i].x < tmin); j++)
			tmin = min(tmin, dist(T[j], T[i])); 
	return tmin;
}

完整代码

#define debug
#ifdef debug
#include <time.h>
#include "/home/majiao/mb.h"
#endif

#include <iostream>
#include <algorithm>
#include <vector>
#include <string.h>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <math.h>

#define MAXN ((int)3e5+7)
#define ll long long int
#define INF (0x7f7f7f7f)
#define fori(lef, rig) for(int i=lef; i<=rig; i++)
#define forj(lef, rig) for(int j=lef; j<=rig; j++)
#define fork(lef, rig) for(int k=lef; k<=rig; k++)
#define QAQ (0)

using namespace std;

#define show(x...) \ do { \ cout << "\033[31;1m " << #x << " -> "; \ err(x); \ } while (0)

void err() { cout << "\033[39;0m" << endl; }
template<typename T, typename... A>
void err(T a, A... x) { cout << a << ' '; err(x...); }

namespace FastIO{

	char print_f[105];
	void read() {}
	void print() { putchar('\n'); }

	template <typename T, typename... T2>
		inline void read(T &x, T2 &... oth) {
			x = 0;
			char ch = getchar();
			ll f = 1;
			while (!isdigit(ch)) {
				if (ch == '-') f *= -1; 
				ch = getchar();
			}
			while (isdigit(ch)) {
				x = x * 10 + ch - 48;
				ch = getchar();
			}
			x *= f;
			read(oth...);
		}
	template <typename T, typename... T2>
		inline void print(T x, T2... oth) {
			ll p3=-1;
			if(x<0) putchar('-'), x=-x;
			do{
				print_f[++p3] = x%10 + 48;
			} while(x/=10);
			while(p3>=0) putchar(print_f[p3--]);
			putchar(' ');
			print(oth...);
		}
} // namespace FastIO
using FastIO::print;
using FastIO::read;

int n, m, Q, K;
struct Node {
	double x, y;
} a[MAXN], T[MAXN];

#define Ax (A.x)
#define Ay (A.y)
#define Bx (B.x)
#define By (B.y)
double dist(Node& A, Node& B) {
	return sqrt((Ax-Bx)*(Ax-Bx) + (Ay-By)*(Ay-By));
}

bool cmpx(Node& A, Node& B) { return Ax==Bx ? Ay < By : Ax < Bx;}

void init() {
	sort(a+1, a+1+n, cmpx);
}

#if 1
//算法导论里的板子
#define DINF (1e18)
double closep(int lef, int rig) {
	if(lef == rig) return DINF;
	if(lef+1 == rig) return dist(a[lef], a[rig]);

	int mid = (lef + rig) >> 1;
	Node midp = a[mid];
	double lmin = closep(lef, mid);
	double rmin = closep(mid+1, rig);

	double tmin = min(lmin, rmin);
	int sz = 0, i = lef, j = mid+1;
	//按y轴递增归并排序
	while(i<=mid && j<=rig) {
		if(a[i].y < a[j].y) T[sz++] = a[i++];
		else T[sz++] = a[j++];
	}
	while(i <= mid) T[sz++] = a[i++];
	while(j <= rig) T[sz++] = a[j++];

	for(i=rig; i>=lef; i--) a[i] = T[--sz];
	sz = 0;
	for(i=lef; i<=rig; i++) //中间线[-tmin, +tmin]范围内的点加入T数组
		if(fabs(a[i].x-midp.x) < tmin) T[sz++] = a[i];
	for(i=0; i<sz; i++) //暴力这个复制出来的T[]数组
		for(j=i+1; j<sz && (T[j].x-T[i].x < tmin); j++)
			tmin = min(tmin, dist(T[j], T[i])); 
	return tmin;
}
#else 

//普通分治 复杂度可以过但是还不够好
int ID[MAXN];
#define Ti(i) (Y[ID[i]])
#define DINF (1e18)

double closep(int lef, int rig) {
	if(lef == rig) return DINF;
	if(lef+1 == rig) return dist(a[lef], a[rig]);
	int mid = (lef + rig) >> 1;

	double lmin = closep(lef, mid);
	double rmin = closep(mid+1, rig);
	double ret = min(lmin, rmin);
	int sz = 0;
	for(int i=lef; i<=rig; i++)
		if(fabs(a[i].x-a[mid].x) <= ret) T[++sz] = a[i];
	sort(T+1, T+1+sz, cmpy); //这个排序可以归并出来,如上
	for(int i=1; i<=sz; i++)
		for(int j=i+1; j<=sz && (T[j].y-T[i].y<=ret); j++)
			ret = min(ret, dist(T[i], T[j]));
	return ret;
}

#endif

int main() {
#ifdef debug
	freopen("/home/majiao/下载/P1429_11.in", "r", stdin);
	freopen("out", "w", stdout);
	clock_t stime = clock();
#endif
	scanf("%d ", &n);
	for(int i=1; i<=n; i++) 
		scanf("%lf %lf ", &a[i].x, &a[i].y);

	init();
	double ans = closep(1, n);
	printf("%.4lf\n", ans);


#ifdef debug
	clock_t etime = clock();
	printf("rum time: %lf 秒\n",(double) (etime-stime)/CLOCKS_PER_SEC);
#endif 
	return 0;
}