洛谷P1257 平面上的最接近点对

题面:

解题:

爆力枚举?

首先,判断一下Brute Force能否解决问题?

对于每个新出现的点d[i],都有与d[0]~d[i-1]间产生答案最短距离的可能性,

已知暴力枚举的时间复杂度为O(n²),对于数据范围n≤10000来说,这样的算法很难受得住考研。

分治+递归

如下图,我们假设二维空间上有0~6共7个点,先将这7个点按x坐标一分为二(橙线),

取0~(0+6)/2 为左半,即点:0 1 2 3 则 4 5 6为右半

再重复上述过程,用蓝线分割,使得每个区间含的点数量不多余2个。

于是,对于含2个点的小区间里,我们求出了两点间距;对于含1个点的区间不操作。

现在,只看橙线左半部分,将0、1区间距离标记d1,2、3区间距离标记为d2

d1d2作为值返回给外层的循环,即包含点0、1、2、3的循环。

接收到d1d2的值后,进行比对,得出d=min(d1d2)=d2,

但,蓝线区间内的最短点组,也可能是由一个左区间的点,一个右区间的点组成的。

为解决此问题,我们选取分割点mid=(0+3)/2=1 点为搜索轴,

搜索右侧与mid点横、纵坐标差均不超过d的点,

即:绿色矩形范围内的点,将这些点全部加入temp;

同样,我们也要搜索左侧与mid点的横、纵坐标差均不超过d的点,加入temp……

构造完temp,再枚举temp中各两点间的元素的距离,

d距离还要小两个点只可能在temp内的两点之间产生

如此,我们得出一套分治+递归算法:通过小区间内产生的d,来缩小更大的区间需要搜索的范围,从而实现减少重复搜索的目的。

AC代码奉上

#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>

using namespace std;

class Dot
{
public:
    double x;    //坐标
    double y;
};

int n;
double ans = 9999999999;
vector<Dot>v;     //储存点
int temp[10005];  //临时数组,存放临时点的索引

class MyCompare
{
public:
    bool operator ()(Dot &d1,Dot &d2)   const
    {
        return d1.x < d2.x;
    }
};

bool cmp(const int &i,const int &j)  //纵坐标降序排序
{
    return v[i].y < v[j].y;
}

double dis(int i, int j) //获取距离
{
    return sqrt(fabs((v[i].x - v[j].x) * (v[i].x - v[j].x) + (v[i].y - v[j].y) * (v[i].y - v[j].y)));
}

double getMinDis(int left, int right)  //返回该区间内点的最小距离
{
    double ret = 10 << 15;  //初始化为无穷大
    if (left == right)return ret;
    else if (left + 1 == right) { return dis(left, right); }
    int mid = (left + right) >> 1; //中值=(left+right)/2
    double dl = getMinDis(left, mid);      //左区间的最短长度
    double dr = getMinDis(mid + 1, right); //右区间的最短长度
    double d = min(dl, dr);//左、右区间最短长度中较短者,将搜索范围缩小至mid-d,mid+d
    cout << "d=" << d << endl;
    int k = 0;    //temp的容量
    for (int i = left; i <= right; i++)
        if (fabs(v[i].x - v[mid].x) <= d)   //存入temp:在mid-d,mid+d的区间内
            temp[k++] = i;

    sort(temp, temp + k, cmp);  //按y排序

    for (int i = 0; i < k; i++)
        for (int j = i + 1; j < k && v[temp[j]].y - v[temp[i]].y <= d; j++)
            d = min(d, dis(temp[i], temp[j]));
    return d;
}

int main()
{
    cin >> n;
    if (n == 1) { cout << "0.0000" << endl; return 0; }  //特判n=1
    for(int i=1;i<=n;i++)
    {
        Dot d;
        cin>>d.x>>d.y;
        v.push_back(d);
    }
    sort(v.begin(), v.end(), MyCompare());//按x排序

    ans = getMinDis(0, n - 1);  //0~n-1,即vector容器的全部元素

    printf("%.4lf", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值