HDU1007——二维空间最近点对问题(分治)

http://acm.hdu.edu.cn/showproblem.php?pid=1007

题意就是给你n个点,输出最近两点距离的一半。

这道题用到了分治的思想。就是将一个大问题划分规模较小的子问题,然后对子问题进一步划分求解,最后将子问题的解进行合并。

这道题目在划分问题上显得并不困难,可以沿着坐标轴进行划分,将点的划分成数量尽量相等的两份,分别求出左右区域的最短距离。但是在进行合并的时候却没有那么容易,如果枚举所有的点,那么效率就太低了。

在求解跨越分界线的点对时,首先可以排除的是与分界线距离超过左右区域内最短距离的点,如果存在大于此距离的点,那么必然不可能成为最近点对。

其次,在对每个点进行遍历时,如果两点的纵坐标大于左右区域内最短距离,那么就不必再搜索纵坐标差值更大的点。

所以基本的流程就是这样:

首先对所有点按X坐标进行排序,找出中点进行划分,左右分别进行求解。

然后找到对左右两侧的最短距离min,舍弃距离中点超过min的点,对剩余点按照Y坐标排序,对每个点进行遍历,如果两点Y轴距离大于min,那么就不再搜索距离更大的点,直接进行下一个点的搜索。

我还加了个小剪枝,就是分治过程中出现min=0,那么就直接返回。好像有那么一点点用,提高了20ms。。。


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

using namespace std;


struct node
{
    double x,y;
}p[100005];
int c[100005],flag;

double min(double a,double b)
{
    return a<b?a:b;
}
bool cmp(node a,node b)
{
    return a.x<b.x;
}
bool cmp2(int a,int b)
{
    return p[a].y<p[b].y;
}
double dis(int a,int b)
{
    return
        sqrt((p[a].x-p[b].x)*(p[a].x-p[b].x)+(p[a].y-p[b].y)*(p[a].y-p[b].y));
}
double merge(int low,int up)
{
    int i,j,mid,cnt=0;
    if(flag==1) return 0;
    if(up-low==2)
        return dis(low,up-1);
    if(up-low==3)
        return min(dis(low,up-1),dis(low,up-2)),dis(up-1,up-2);
    mid=(up+low)/2;
    double ans=min(merge(low,mid),merge(mid,up));
    if(ans==0) flag==1;
    for(i=low;i<up;i++)
        if(p[i].x>p[mid].x-ans&&p[i].x<p[mid].x+ans)
            c[cnt++]=i;
    sort(c,c+cnt,cmp2);
    for(i=0;i<cnt;i++)
    {
        for(j=i+1;j<cnt;j++)
        {
            if(p[c[j]].y-p[c[i]].y>=ans)
                break;
            ans=min(ans,dis(c[i],c[j]));
        }

    }
    return ans;
}
int main()
{
    int n,i;
    while(scanf("%d",&n)&&n)
    {
        flag=0;
        for(i=0;i<n;i++)
        {
            scanf("%lf%lf",&p[i].x,&p[i].y);
        }
        sort(p,p+n,cmp);
        printf("%.2lf\n",merge(0,n)/2);
    }

	return 0;
}





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值