最邻近点对问题

问题描述:

        给定平面上n个点,找其中的一对点,使得在n个点的所有点对中,该点对的距离最小。

解题步骤:

        第一次分割时,将所有点集合S分割为左右两个子集合S1和S2,分别输出左右子集合S1和S2,以及所有点集合S的最接近点对的距离以及最接近点对。

        取第n/2个点的x坐标,比较所有在【x-d,x+d】内点的距离,取【x-d,x】内的一点,将该点与x属于【x,x+d】,【y-d,y+d】内的点进行比较(最多个数为6个)。

        比较【x-d,x+d】内求得的最短距离与d进行,得出所有点的最邻近点对。

        鸽舍原理:由d的意义可知P2中任何2个S中的点的距离都不小于d。由此可以推出矩形R中最多只有6个S中的点。事实上,我们可以将矩形R的长为2δ的边3等分,将它的长为d的边2等分,由此导出6个(d/2)×(2d/3)的矩形

代码 

#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
struct Point {
    double x;
    double y;
};

// 计算两个点之间的欧几里得距离
double calculateDistance(Point a, Point b) {
    double dx = a.x - b.x;
    double dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
}

//让X进行升序排序
bool comparePointsX(const Point& p1, const Point& p2) {
    return p1.x < p2.x;
}

double zuiJingDiandui(vector<Point> points)
{

    int n = points.size();
    double minDistance;
    if (n <= 1)
    {

        return 0;
    }

    if (n == 2)
    {
        double d1 = calculateDistance(points[0], points[1]);
        return d1;
    }

    if (n == 3)
    {
        double d1 = calculateDistance(points[0], points[1]);
        double d2 = calculateDistance(points[1], points[2]);
        double d3 = calculateDistance(points[2], points[3]);
        return min(min(d1, d2), min(d2, d3));
    }
    if (n > 3)
    {

        // 按照x坐标排序
        sort(points.begin(), points.end(), comparePointsX);

        // 分割点集
        int mid = n / 2; // 找到中间点索引
        vector<Point> leftPoints(points.begin(), points.begin() + mid); // 分割出左侧的点集
        vector<Point> rightPoints(points.begin() + mid, points.end()); // 分割出右侧的点集

        // 递归计算左右子集的最近点距离
        double leftDistance = zuiJingDiandui(leftPoints);
        double rightDistance = zuiJingDiandui(rightPoints);

        // 找到左右子集的最小距离
        minDistance = min(leftDistance, rightDistance);


        // 找到可能跨越子集边界的最近点对
        vector<Point> stripPoints;
        double midLine = points[mid].x; // 中线的x坐标
        for (int i = 0; i < n; i++) {
            if (fabs(points[i].x - midLine) < minDistance) { // 将横坐标与中线横坐标距离小于最小距离的点放入stripPoints
                stripPoints.push_back(points[i]);
            }
        }

        // 在stripPoints中寻找更小的距离
        int stripSize = stripPoints.size();
        for (int i = 0; i < stripSize; i++) {
            for (int j = i + 1; j < stripSize && (stripPoints[j].y - stripPoints[i].y) < minDistance; j++) {
                double distance = calculateDistance(stripPoints[i], stripPoints[j]);
                minDistance = min(minDistance, distance);
            }
        }
    }
    return  minDistance;
}

int  main()
{
    int N;
    cin >> N;
    vector<Point> points;
    double x, y;
    for (int i = 0; i < N; i++)
    {
        cin >> x >> y;
        points.push_back(Point{ x, y });
    }
    if (zuiJingDiandui(points) == 0)
    {
        cout << "不存在" << endl;
    }
    else
        cout  << zuiJingDiandui(points) << endl;

    return 0;

}

结果

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用分治算法来求解最邻近问题。具体实现步骤如下: 1. 将所有按照横坐标排序,如果横坐标相同则按照纵坐标排序。 2. 将集分成左右两个部分,分别递归求解左右两个部分的最邻近对。 3. 计算左右两个部分中距离最小的对,记为d。 4. 将所有横坐标与d的横坐标之差小于d的按照纵坐标排序。 5. 在排序后的集中,对于每个,只需要考虑与其纵坐标之差小于d的的距离即可。 6. 对于每个,只需要考虑与其纵坐标之差小于d的的距离即可。 7. 在这些中,找到距离最小的对,记为d'。 8. 返回d和d'中距离最小的对。 以下是C语言的代码实现: ```c #include <stdio.h> #include <stdlib.h> #include <math.h> #define MAX_N 100000 typedef struct { double x, y; } Point; int cmp_x(const void *a, const void *b) { Point *p1 = (Point *)a; Point *p2 = (Point *)b; if (p1->x < p2->x) return -1; if (p1->x > p2->x) return 1; if (p1->y < p2->y) return -1; if (p1->y > p2->y) return 1; return 0; } int cmp_y(const void *a, const void *b) { Point *p1 = (Point *)a; Point *p2 = (Point *)b; if (p1->y < p2->y) return -1; if (p1->y > p2->y) return 1; if (p1->x < p2->x) return -1; if (p1->x > p2->x) return 1; return 0; } double dist(Point p1, Point p2) { double dx = p1.x - p2.x; double dy = p1.y - p2.y; return sqrt(dx * dx + dy * dy); } double closest_pair(Point *p, int n) { if (n <= 1) return INFINITY; int m = n / 2; double x = p[m].x; double d = fmin(closest_pair(p, m), closest_pair(p + m, n - m)); qsort(p, n, sizeof(Point), cmp_y); Point *t = (Point *)malloc(n * sizeof(Point)); int k = 0; for (int i = 0; i < n; i++) { if (fabs(p[i].x - x) >= d) continue; for (int j = k - 1; j >= 0; j--) { double dy = p[i].y - t[j].y; if (dy >= d) break; double td = dist(p[i], t[j]); if (td < d) d = td; } t[k++] = p[i]; } free(t); return d; } int main() { int n; Point p[MAX_N]; scanf("%d", &n); for (int i = 0; i < n; i++) { scanf("%lf%lf", &p[i].x, &p[i].y); } qsort(p, n, sizeof(Point), cmp_x); printf("%.2f\n", closest_pair(p, n)); return 0; } ``` 该算法的时间复杂度为O(nlogn)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值