分治 -- 平面最近点对

平面最近点对 :

分析各种情况 :

首先将所有点对按照 x 作为第一关键字进行排序,然后从中间进行劈开,进行递归分治
最后答案就是 res = min(l -- mid,mid + 1 -- r);

从上图可以得知 : 要求在这个平面内所有点中的最近点对,会有三种情况:
                1、两个点都在左侧
                2、两个点都在右侧
                3、一个点在左侧,一个点在右侧

分别向两侧递归进行分治 :

寻找最近点对 :

1、找到左侧的最近点对
2、找到右侧的最近点对
进行比较我们就会得到一个左侧和右侧之中的最近的一对
这时我们会得到一个最近点对的距离(是目前这个过程中最近的一对,假设 当前最近点对的距离是 res)
还差第三种情况 :  第三种情况是一个在左侧,一个在右侧,那么这些点一定是靠近 中间的 mid 的,
所以第三种情况的所有点一定是下面这种情况 (虚线的范围代表这些点可能会出现的范围): 

由于我们得到的最近点对的距离是 res,所以剩下的距离一定是要 < res 的,最多 == res,所以我们可以
求出在这个范围内与每个点可能会组成的点的数量,然后,按照纵坐标排序枚举这些(数量不会太多,不用担心会超时)
(如上图所示,最多只会有 6 个点 -- 每个方格内最多放一个点)

Code :

#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

#define INF 0x3f3f3f3f

using namespace std;

const int SIZE = 1e5 + 10;
struct Point {
    double x,y;

    bool operator <( const Point &W)const {
        return x < W.x;
    }
} Points[SIZE],temp[SIZE];
int t,n;

int main(void) {
    double DFS(int l,int r);
    while(scanf("%d",&n) != EOF) {
        if(n == 0) break;
        for(int i = 1; i <= n; i ++) {
            scanf("%lf%lf",&Points[i].x,&Points[i].y);
        }
                // 先将所有坐标按照横坐标进行排序--便于分治
        sort(Points + 1,Points + 1 + n);
        printf("%.2lf\n",DFS(1,n));
    }
    return 0;
}

// 求两个点之间的距离

double dist(Point a,Point b) {
    double dx = a.x - b.x;
    double dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
}

double DFS(int l,int r) {
        // 将和自身的距离设为 无穷大
    if(l >= r) return INF;
        // 就两个点时直接返回两个点之间的距离即可
    if(l + 1 == r) return dist(Points[l],Points[r]);
    int mid = l + r >> 1;
        // 中间的 mid 部分,用于求第三种情况
    int mid_x = Points[mid].x;
        // 递归找到左右两端的最近点对
    double res = min(DFS(l,mid),DFS(mid + 1,r));
    {        // 归并排序(便于查找,省时间,不用每次都排序了)
        int k = 0,i = l,j = mid + 1;
        while(i <= mid && j <= r) {
            if(Points[i].y > Points[j].y) {
                temp[k ++] = Points[j ++];
            } else {
                temp[k ++] = Points[i ++];
            }
        }
        while(i <= mid) {
            temp[k ++] = Points[i ++];
        }
        while(j <= r) {
            temp[k ++] = Points[j ++];
        }
        for(int i = l; i <= r; i ++) {
            Points[i] = temp[i - l];
        }
    }
    int k = 0;
        // 寻找区域内的所有点
    for(int i = l; i <= r; i ++) {
        if(Points[i].x >= mid_x - res && Points[i].x <= mid_x + res) {
            temp[k ++] = Points[i];
        }
    }
        // 枚举区域内的所有点,寻找更近的点对
    for(int i = 0; i < k; i ++) {
        for(int j = i - 1; j >= 0 && temp[i].y - temp[j].y < res; j --) {
            res = min(res,dist(temp[i],temp[j]));
        }
    }
    return res;
}

例题:

1、Quoit Design

#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

#define INF 0x3f3f3f3f

using namespace std;

const int SIZE = 1e5 + 10;
struct Point {
    double x,y;

    bool operator <( const Point &W)const {
        return x < W.x;
    }
} Points[SIZE],temp[SIZE];
int t,n;

int main(void) {
    double DFS(int l,int r);
    while(scanf("%d",&n) != EOF) {
        if(n == 0) break;
        for(int i = 1; i <= n; i ++) {
            scanf("%lf%lf",&Points[i].x,&Points[i].y);
        }
        sort(Points + 1,Points + 1 + n);
        printf("%.2lf\n",DFS(1,n) / 2);
    }
    return 0;
}

double dist(Point a,Point b) {
    double dx = a.x - b.x;
    double dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
}

double DFS(int l,int r) {
    if(l >= r) return INF;
    if(l + 1 == r) return dist(Points[l],Points[r]);
    int mid = l + r >> 1;
    int mid_x = Points[mid].x;
    double res = min(DFS(l,mid),DFS(mid + 1,r));
    {
        int k = 0,i = l,j = mid + 1;
        while(i <= mid && j <= r) {
            if(Points[i].y > Points[j].y) {
                temp[k ++] = Points[j ++];
            } else {
                temp[k ++] = Points[i ++];
            }
        }
        while(i <= mid) {
            temp[k ++] = Points[i ++];
        }
        while(j <= r) {
            temp[k ++] = Points[j ++];
        }
        for(int i = l; i <= r; i ++) {
            Points[i] = temp[i - l];
        }
    }
    int k = 0;
    for(int i = l; i <= r; i ++) {
        if(Points[i].x >= mid_x - res && Points[i].x <= mid_x + res) {
            temp[k ++] = Points[i];
        }
    }
    for(int i = 0; i < k; i ++) {
        for(int j = i - 1; j >= 0 && temp[i].y - temp[j].y < res; j --) {
            res = min(res,dist(temp[i],temp[j]));
        }
    }
    return res;
}

2、袭击

#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

#define INF 0x3f3f3f3f

using namespace std;

const int maxn = 1e5 + 10;

struct Point{
    double x,y;
    int type;

    bool operator<(const Point &w) const {
        return x < w.x;
    }

}Points[maxn << 1],temp[maxn << 1];

int T,n;

int main(void) {
    double DFS(int l,int r);
    scanf("%d",&T);
    while(T --) {
        scanf("%d",&n);
        for(int i = 1; i <= n; i ++) {
            scanf("%lf%lf",&Points[i].x,&Points[i].y);
            Points[i].type = 0;
        }
        for(int i = n + 1; i <= n * 2; i ++) {
            scanf("%lf%lf",&Points[i].x,&Points[i].y);
            Points[i].type = 1;
        }
        sort(Points + 1,Points + (n << 1));
        printf("%.3lf\n",DFS(1,n << 1));
    }

    return 0;
} 

double dist(Point a,Point b) {
    if(a.type == b.type) return INF;
    double dx = (a.x - b.x) * (a.x - b.x);
    double dy = (a.y - b.y) * (a.y - b.y);
    return sqrt(dx + dy);
}

double DFS(int l,int r) {
    if(l >= r) return INF;
    if(l + 1 == r) return dist(Points[l],Points[r]);
    int mid = l + r >>  1;
    double mid_x = Points[mid].x;
    double res = min(DFS(l,mid),DFS(mid + 1,r));
    {
        int i = l,j = mid + 1,k = 0;
        while(i <= mid && j <= r) {
            if(Points[i].y > Points[j].y) {
                temp[k ++] = Points[j ++];
            } else {
                temp[k ++] = Points[i ++];
            }
        }
        while(i <= mid) {
            temp[k ++] = Points[i ++];
        }
        while(j <= r) {
            temp[k ++] = Points[j ++];
        }
        for(int i = l; i <= r; i ++) {
            Points[i] = temp[i - l];
        }
    }
    int k = 0;
    for(int i = l; i <= r; i ++) {
        if(Points[i].x >= mid_x - res && Points[i].x <= mid_x + res) {
            temp[k ++] = Points[i];
        }
    }
    for(int i = 0; i < k; i ++) {
        for(int j = i - 1; j >= 0 && temp[i].y - temp[j].y < res; j --) {
            res = min(res,dist(temp[i],temp[j]));
        }
    }
    return res;
}

原文地址:https://www.cnblogs.com/prjruckyone/p/12345265.html

时间: 2024-10-18 12:04:30

分治 -- 平面最近点对的相关文章

$Poj3714/AcWing\ Raid$ 分治/平面最近点对

$AcWing$ $Sol$ 平面最近点对板子题,注意要求的是两种不同的点之间的距离. $Code$ #include<bits/stdc++.h> #define il inline #define Rg register #define go(i,a,b) for(Rg int i=a;i<=b;++i) #define yes(i,a,b) for(Rg int i=a;i>=b;--i) #define mem(a,b) memset(a,b,sizeof(a)) #def

平面最近点对(分治nlogn)

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

计算几何 平面最近点对 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 1429 平面最近点对 | 平面分治

Luogu 1429 平面最近点对 题目描述 给定平面上n个点,找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的 输入输出格式 输入格式: 第一行:n:2≤n≤200000 接下来n行:每行两个实数:x y,表示一个点的行坐标和列坐标,中间用一个空格隔开. 输出格式: 仅一行,一个实数,表示最短距离,精确到小数点后面4位. 这是一道平面上的分治. 这是一个平面,我们把它分成两半,使x坐标位于最中间的两个点分到左右两侧: 对于同在左侧或同在右侧的点对,我们可以递归处理:

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

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

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

P1429 平面最近点对(加强版) 主要思路: 分治,将点按横坐标为第1关键字升序排列,纵坐标为第2关键字升序排列,进入左半边和右半边进行分治. 设d为左右半边的最小点对值.然后以mid这个点为中心,扩展宽为2d,长为2d的正方形.除了这个正方形外的点都不可能使答案更小.而且这个正方形里至多8个点(可以证明至多6个,我不会.but,知道至多8个就够了,这样已经保证了复杂度.)一句话证明:如果多余8个点,那么必有2个点的最小距离比d小.这8个点内暴力枚举就好了. //luoguP1429 #inc

poj3714 Raid(分治求平面最近点对)

题目链接:https://vjudge.net/problem/POJ-3714 题意:给定两个点集,求最短距离. 思路:在平面最近点对基础上加了个条件,我么不访用f做标记,集合1的f为1,集合2的f为-1,那么求两个点的距离时,如果a.f*b.f=-1时计算距离,否则乘积为1的话返回inf.其它就和hdoj1007一样了. AC代码: #include<cstdio> #include<algorithm> #include<cmath> #include<cs

HDU1007--Quoit Design(平面最近点对)

Problem Description Have you ever played quoit in a playground? Quoit is a game in which flat rings are pitched at some toys, with all the toys encircled awarded.In the field of Cyberground, the position of each toy is fixed, and the ring is carefull