hdu(1007) 最近点对 分治法

本文介绍了一种使用分治算法解决最近点对问题的方法。通过将问题分解成子问题,并利用递归策略,有效地降低了时间复杂度。文章详细解释了算法流程,并提供了完整的C++实现代码。

    最近点对一般想到枚举  ,一一枚举时间复杂度为n^2;枚举时候一些操作是多余的,有了分治算法的思想 ,把一些问题分个击破,再回到整体。


题目链接


  以这道题为例,我们可以把他按照x轴的升序分成多个子区域先在子区域中求最近点距离,然后将相邻两个子区域合并,看看两个子区域中有没有更小的。大致思想就是这样的。

设计算法:递归将问题分成一小个问题。在找区域里面的最近点先将他按照x或y轴升或者降序排序这样找就可以省时间,详见代码,先看题,再看代码。


有错的地方还望指出,多多来hack

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <algorithm>
#include <stdlib.h>
#include <cmath>

using namespace std;
const int MM = 100005;
const double INF=1e20;
struct Points
{
    double x,y;
};
Points point[MM];
bool cmpx(Points x,Points y)
{
    return x.x<y.x;
}
bool cmpy(int a,int b)
{
    return point[a].y<point[b].y;
}
int n,index[MM];
double dist(Points a,Points b)//返回两点距离 double 类型
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double closest_point(int l,int r)  //--------------------
{
    double d=INF,d1,d2;
    if(l==r) return d;
    else if(l+1==r) return dist(point[l],point[r]);
    int mid=(l+r)>>1;
    d=min(closest_point(l,mid),closest_point(mid+1,r));//递归分解问题,找到子区域中的最近点距离
    /*****************找到后,以他们的最近距离来分割区间************************/
    int i,j,cut=0;
    for(i=l; i<=r; i++)
    {
        if(fabs(point[i].x-point[mid].x)<d)
            index[cut++]=i;
    }
    //在区间里找x宽度小于最近距离的把他的下标存在index数组里面

    sort(index,index+cut,cmpy);
    for(i=0; i<cut; i++)
    {
        for(j=i+1; j<cut; j++)
        {
            if(fabs(point[index[i]].y-point[index[j]].y)>=d)
                break;
            //sort之后,只要当前超过了了,后面的数字一点不可能比这个小,所以不找,省时间
            d=min(dist(point[index[i]],point[index[j]]),d);
        }
    }
    return d;
}
int main()
{
    while(scanf("%d",&n)!=EOF&&n)
    {
        int i;
        for(i=0; i<n; i++)
        {
            scanf("%lf %lf",&point[i].x,&point[i].y);
        }
        sort(point,point+n,cmpx);
        printf("%.2lf\n",closest_point(0,n-1)/2);
    }
    return 0;
}


分治法解决最近点对问题的基本思路是将点集按照 x 坐标排序后,分成左右两部分,分别求出左右两部分的最近点对距离,再考虑跨越左右两部分的最近点对距离,最后取三者中的最小值。 以下是使用分治法解决最近点对问题的 C 语言代码实现: ```c #include <stdio.h> #include <stdlib.h> #include <math.h> // 定义点的结构体 typedef struct { double x; double y; } Point; // 比较函数,用于 qsort 按 x 坐标排序 int compareX(const void* a, const void* b) { Point *p1 = (Point *)a, *p2 = (Point *)b; return (p1->x - p2->x); } // 比较函数,用于 qsort 按 y 坐标排序 int compareY(const void* a, const void* b) { Point *p1 = (Point *)a, *p2 = (Point *)b; return (p1->y - p2->y); } // 计算两点之间的距离 double distance(Point p1, Point p2) { return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)); } // 分治法解决最近点对问题 double closestUtil(Point P[], int n) { if (n <= 3) { double min = 99999; for (int i = 0; i < n; ++i) for (int j = i + 1; j < n; ++j) if (distance(P[i], P[j]) < min) min = distance(P[i], P[j]); return min; } int mid = n / 2; Point midPoint = P[mid]; double dl = closestUtil(P, mid); double dr = closestUtil(P + mid, n - mid); double d = (dl < dr)? dl : dr; Point strip[1000]; int j = 0; for (int i = 0; i < n; i++) if (fabs(P[i].x - midPoint.x) < d) strip[j] = P[i], j++; qsort(strip, j, sizeof(Point), compareY); for (int i = 0; i < j; ++i) for (int k = i + 1; k < j && (strip[k].y - strip[i].y) < d; ++k) if (distance(strip[i], strip[k]) < d) d = distance(strip[i], strip[k]); return d; } double closest(Point P[], int n) { qsort(P, n, sizeof(Point), compareX); return closestUtil(P, n); } int main() { Point P[] = {{2, 3}, {12, 30}, {40, 50}, {5, 1}, {12, 10}, {3, 4}}; int n = sizeof(P) / sizeof(P[0]); printf("The smallest distance is %lf\n", closest(P, n)); return 0; } ``` ### 代码解释 1. **结构体定义**:定义了 `Point` 结构体来表示二维平面上的点,包含 `x` 和 `y` 坐标。 2. **比较函数**:`compareX` 和 `compareY` 分别用于按 `x` 坐标和 `y` 坐标对 `Point` 数组进行排序。 3. **距离计算函数**:`distance` 函数用于计算两点之间的欧几里得距离。 4. **分治函数**:`closestUtil` 是核心的分治函数,它将点集分成左右两部分,递归地求解左右两部分的最近点对距离,然后考虑跨越左右两部分的最近点对距离。 5. **主函数**:`closest` 函数先按 `x` 坐标对所有点进行排序,然后调用 `closestUtil` 函数求解最近点对距离。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值