洛谷 P1429 平面最近点对(加强版) 分治
题目描述
给定平面上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
步骤如下:
-
递归出口是 left==right 和 left>right
-
每次递归可以得到
-
两个点都在左边最小距离 DistLef
-
两个点都在右边最小距离 DistRig
-
一个点在左边,一个点在右边的最小距离 DistMid
-
3者取最小返回
-
return min(DistLef, DistRig, 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;
}