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

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

平面最近点对顾名思义就是在一个二维平面内找到距离最近的两个点。用到分治思想,可以很好的减少复杂度。
接下来进行解析。

  1. 首先将输入的坐标按照x坐标大小排序,并二分为左右两块区域
    在这里插入图片描述
  2. 在左右两块区域都可以得到一个最近点对距离,取最小值作为暂时的ans在这里插入图片描述
  3. 考虑完左右两边的情况以后再考虑两个点分别在左右的情况,很显然这种情况就不用所有点都考虑,那就没必要二分了。我们以中间的点为标准,划分一个新区域,区域范围为中间点向左右都取ans’的大小在这里插入图片描述
  4. 为了进一步减少计算量,我们可以在y坐标上下功夫,可以只考虑两点垂直距离不超过ans’大小的两个点,因为超过的话按勾股定理两点距离是不可能比ans’更小的。在这里插入图片描述
  5. 最后,取min(ans3,ans’)即可得出答案

接下来是源代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;

#define MAXN 10000
#define INF 0x7fffffff

struct point{
    double x,y;
}a[MAXN];

int N;
int t[MAXN];

//以x坐标大小为关键词
bool cmp1(point x,point y){
    return x.x<y.x;
}
//以y坐标大小为关键词
bool cmp2(int x,int y){
    return a[x].y<a[y].y;
}

//计算两点距离
double dis(point x,point y){
    return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));
}

//核心算法  分治思想
double F(int l,int r){
    if(r-l==0)
        return INF;
    if(r-l==1)  //如果递归完后直接输出距离
        return dis(a[l],a[r]);
    int mid=(l+r)>>1;
    double ans=min(F(l,mid),F(mid+1,r));
    int cnt=0;
    for(int i=l;i<=r;i++)
        //还有一种情况是距离最小的两点刚好分在mid两端ans距离内的点
        if(a[i].x>=a[mid].x-ans&&a[i].x<=a[mid].x+ans)
            t[++cnt]=i;
    sort(t+1,t+cnt+1,cmp2); //以y坐标大小排序
    for(int i=1;i<=cnt;i++)
        for(int j=i+1;j<=cnt;j++){
            if(a[t[j]].y>=a[t[i]].y+ans) break; //两个点的垂直距离超过ans就不必计算了,显然不可能会成为新的ans
            ans=min(ans,dis(a[t[i]],a[t[j]]));
        }
    return ans;
}

int main(){

    cin >> N;   //输入坐标数
    for(int i=1;i<=N;i++)
        cin >> a[i].x >> a[i].y;    //输入坐标
    sort(a+1,a+N+1,cmp1);   //以x坐标大小排序
    cout << F(1,N) << endl;     //输出最小点对距离
}

  • 10
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最近问题是指在平面上给定N个,求其中距离最近的两个之间的距离。分治法是一种常用的解决最近问题算法分治法的基本思想是将问题分解成多个子问题,然后递归地解决这些子问题,最后将子问题的解合并起来得到原问题的解。对于最近问题,我们可以采用以下的分治算法: 1. 将所有按照横坐标从小到大排序,然后将它们划分为两部分,分别处理左右两个部分。 2. 对于左右两个部分,分别递归求解最近对距离。 3. 将左右两个部分中距离最近对的距离记为d。 4. 扫描所有横坐标在中间区域内的,按照纵坐标从小到大排序。对于每个,只需考虑它与纵坐标差不超过d的之间的距离,因为这些是可能成为最近对的候选者。 5. 对于每个,只需要考虑它与后面7个之间的距离即可,因为如果有距离更近的,它们之间的距离一定小于d。 6. 扫描完中间区域内的所有后,最近对的距离就是左右两个部分中的最小值和中间区域内的最小值中的较小值。 下面是该算法的伪代码: ``` function closest_pair(points): // 按照横坐标排序 sort(points, key=lambda p: p.x) // 递归终止条件 if len(points) <= 3: return brute_force(points) // 求解左右两个部分的最近对距离 mid = len(points) // 2 left_points = points[:mid] right_points = points[mid:] left_min_dist = closest_pair(left_points) right_min_dist = closest_pair(right_points) // 求解中间区域的最近对距离 min_dist = min(left_min_dist, right_min_dist) strip_points = [p for p in points if abs(p.x - points[mid].x) < min_dist] strip_points.sort(key=lambda p: p.y) for i in range(len(strip_points)): for j in range(i+1, min(i+8, len(strip_points))): dist = distance(strip_points[i], strip_points[j]) min_dist = min(min_dist, dist) return min_dist ``` 其中,brute_force是暴力求解最近对距离的函数,distance是计算两个之间距离的函数。时间复杂度约为O(N log N)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值