Luogu 1429 平面最近点对 | 平面分治

Luogu 1429 平面最近点对

题目描述
给定平面上n个点,找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的
输入输出格式
输入格式:
第一行:n;2≤n≤200000
接下来n行:每行两个实数:x y,表示一个点的行坐标和列坐标,中间用一个空格隔开。
输出格式:
仅一行,一个实数,表示最短距离,精确到小数点后面4位。

这是一道平面上的分治。

这是一个平面,我们把它分成两半,使x坐标位于最中间的两个点分到左右两侧:

对于同在左侧或同在右侧的点对,我们可以递归处理;对于分别位于两侧的点对,如何处理呢?

设递归处理后我们知道同在左侧和同在右侧的点对中,最小距离是d;那么需要枚举的“分别位于两侧的点对”的两个端点的横坐标一定都位于中线左/右距离不超过d的范围内。

当枚举左侧的一个点的时候,右侧只需要找y坐标更小,且y坐标相差不超过d的点,与左侧的点配对。

有了以上两条限制,对于一个点p,另一侧需要与它配对的点不超过6个。

至于具体实现,要先把所有点按照x坐标排序,然后再递归的过程中按照y坐标排序。子区间内部点的顺序被修改(从按x排序变成按y排序),并不会影响母区间的划分,因为在递归进入子区间前母区间已经划分好了。

AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define space putchar(' ')
#define enter putchar('\n')
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
template <class T>
void read(T &x){
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
    if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
    x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x){
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}

const int N = 200005;
int n;
struct point {
    double x, y;
    point operator - (const point &b){
    return (point){x - b.x, y - b.y};
    }
    double norm(){
    return sqrt(x * x + y * y);
    }
    bool operator < (const point &b) const{
    return x < b.x;
    }
} p[N], a[N], b[N], c[N];

double solve(int l, int r){
    if(l >= r) return 1e20;
    int mid = (l + r) >> 1;
    double xmid = (p[mid].x + p[mid + 1].x) / 2;
    double d = min(solve(l, mid), solve(mid + 1, r));
    int pos = l, pb = 0, pc = 0, pl = l, pr = mid + 1;
    while(pos <= r){
    if(pl <= mid && (pr > r || p[pl].y < p[pr].y)){
        if(p[pl].x > xmid - d) b[++pb] = p[pl];
        a[pos++] = p[pl++];
    }
    else{
        if(p[pr].x < xmid + d) c[++pc] = p[pr];
        a[pos++] = p[pr++];
    }
    }
    for(int i = l; i <= r; i++) a[i] = p[i];
    for(int i = 1, j = 1; i <= pb || j <= pc;){
    if(i <= pb && (j > pc || b[i].y < c[j].y)){
        for(int k = j - 1; k && b[i].y - c[k].y < d; k--)
        d = min(d, (b[i] - c[k]).norm());
        i++;
    }
    else{
        for(int k = i - 1; k && c[j].y - b[k].y < d; k--)
        d = min(d, (c[j] - b[k]).norm());
        j++;
    }
    }
    return d;
}

int main(){

    read(n);
    for(int i = 1; i <= n; i++)
    scanf("%lf%lf", &p[i].x, &p[i].y);
    sort(p + 1, p + n + 1);
    printf("%.4lf\n", solve(1, n));

    return 0;
}
时间: 2024-10-24 11:25:43

Luogu 1429 平面最近点对 | 平面分治的相关文章

计算几何 平面最近点对 nlogn分治算法 求平面中距离最近的两点

平面最近点对,即平面中距离最近的两点 分治算法: int SOLVE(int left,int right)//求解点集中区间[left,right]中的最近点对 { double ans; //answer 0)    调用前的预处理:对所有点排序,以x为第一关键词y为第二关键字 , 从小到大; 1)    将所有点按x坐标分成左右两部分; /*      分析当前集合[left,right]中的最近点对,有两种可能: 1. 当前集合中的最近点对,点对的两点同属于集合[left,mid]或同属

平面最近点对问题(分治)

平面最近点对问题是指:在给出的同一个平面内的所有点的坐标,然后找出这些点中最近的两个点的距离. 方法1:穷举 1)算法描述:已知集合S中有n个点,一共可以组成n(n-1)/2对点对,蛮力法就是对这n(n-1)/2对点对逐对进行距离计算,通过循环求得点集中的最近点对2)算法时间复杂度:算法一共要执行 n(n-1)/2次循环,因此算法复杂度为O(n2) 代码实现: 利用两个for循环可实现所有点的配对,每次配对算出距离然后更新最短距离. for (i=0 ; i < n ;i ++){ for(j=

Luogu P1429 平面最近点对 【分治】By cellur925

题目传送门 题目大意:给定平面上n个点,找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的.$n$<=100000. $Algorithm$ 最朴素的$n^2$枚举肯定是不行了,我们在这个数量级只能考虑$nlogn$做法.那么与这个数量级比较相关的也就是分治了. 把整个平面分为两个部分,分别求出两个部分点对间最小的距离,之后再处理跨区域的情况. ? 分治法求解步骤: O(NlogN)  by hzwer1 将点集 S 分为两个?集 SL 和 SR 分别求解2 记 δ 为

求平面最近点对(分治与递归,注:最远点对用凸包求)

Quoit Design Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 36793    Accepted Submission(s): 9552 Problem Description Have you ever played quoit in a playground? Quoit is a game in which flat

平面最近点对(分治)

题目描述 给定平面上n个点,找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的 输入输出格式 输入格式: 第一行:n:2≤n≤200000 接下来n行:每行两个实数:x y,表示一个点的行坐标和列坐标,中间用一个空格隔开. 输出格式: 仅一行,一个实数,表示最短距离,精确到小数点后面4位. 1 //分治基础题 2 #include<cmath> 3 #include<vector> 4 #include<cstdio> 5 #include&l

POJ 3714 Raid 平面最近点对

题目大意:给出两个集合的点,问这两个集合之间最近的点对的距离是多少. 思路:先要知道平面最近点对的分治算法,剩下的就简单了,只需要在更新答案的时候判断一下两个点是否属于两个集合就可以了. 分治算法总是十分神奇的. 对于平面最近点对,首先按照x坐标排序,然后递归进行分治,每次分治时,先获得分治得到的结果,然后按照这个结果来计算本区间.由于我们只需要计算答案小于这个结果的点对就行了,其中(l,mid)和(mid + 1,r)我们已经得到答案,只需要统计一个点在(l,mid),另一个点在(mid +

平面最近点对(分治nlogn)

平面最近点对,是指给出平面上的n个点,寻找点对间的最小距离 首先可以对按照x为第一关键字排序,然后每次按照x进行分治,左边求出一个最短距离d1,右边也求出一个最短距离d2,那么取d=min(d1, d2) 然后只需考虑横跨左右两侧的点,不妨枚举左侧的点pi 那么很显然的是如果pi距离中间的点超过了d,便可以直接舍去,只需考虑距离中间点小于d的点 这样一来就可以对每个pi画一个边长为2d的正方形,易证,矩形内最多存在8个点. 那么关键问题就是要快速找这8个点 朴素做法是对分治后的点进行快排,这样复

牛客练习赛11 B trie树+拓扑判环 E 分治求平面最近点对

牛客练习赛11 B  假的字符串题意:给定n个字符串,互不相等,你可以任意指定字符之间的大小关系(即重定义字典序),求有多少个串可能成为字典序最小的串,并输出它们. tags:好题 对于一个字符串, 1]如有其它字符串是它的前缀,那肯定不可能.这个直接用字典树处理就可以. 2]但如果以这个字符串为最小,怎么判定其它字符串不会矛盾呢? 其实矛盾的情况详细一点说是: 比如要以  abcd 为最小, 但又有另一个字符串 aba ,这就矛盾了. 对这种情况,在跑字典树的时候,我们对有相同父亲结点的多个儿

Luogu P1429 平面最近点对(加强版)

P1429 平面最近点对(加强版) 题意 题目描述 给定平面上\(n\)个点,找出其中的一对点的距离,使得在这\(n\)个点的所有点对中,该距离为所有点对中最小的. 输入输出格式 输入格式: 第一行:\(n\):\(2\leq n\leq 200000\) 接下来\(n\)行:每行两个实数:\(x\ y\),表示一个点的行坐标和列坐标,中间用一个空格隔开. 输出格式: 仅一行,一个实数,表示最短距离,精确到小数点后面\(4\)位. 输入输出样例 输入样例#1: 3 1 1 1 2 2 2 输出样