hdu1007 Quoit Design

本文介绍了如何通过分治思想解决二维平面上N个点的最近点对问题,从O(N^2)降低到O(N*logN*logN)的时间复杂度。通过将点按x轴划分并比较相邻点的距离,再对符合条件的点对进行最终确认,实现高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        首先说一下这题的题意,题意很简单,就是给出平面上N个点的横坐标与纵坐标,求最近的两点的距离(2<=N<=1000000)。

        这题首先很容易想到朴素的方法:分别枚举两个点,通过比较找出其中的最小值,这是O(N^2)的算法,对于N<=1000000的数据范围,是肯定会超时的,这就要我们想其他方法。

        那么现在介绍的就是一个高效求最近点对的方法。一看这数据范围,就会想到这应该是一个O(nlogn)的算法。那么首先想到的应该就是分治的思想,我们对于x轴,将平面上的点分成两部分,如下图所示:


        我们可以很容易想到,最近点对的分布有三种可能性,第一种就是两个点均在mid的左边,第二种就是两个点均在mid的右边,第三种情况就是这两个点分别分布在mid的左边和右边。第一种情况和第二种情况的最近点,可以通过比较相邻的点来获得,我们记得到的最近点的距离为d。接下来最困难的部分就是合并了,我们知道,两个点坐标的距离公式是(x1-x2)^2-(y1-y2)^2,也就是说,这两个点横坐标的距离和纵坐标的距离都要小于d,这样这两个点才有可能是最近点对,否则不可能为最近点对,这样我们就进行了一个类似于剪枝的操作,算法的复杂度也就在这个处理上由O(n^2)降到了O(n*logn*logn),接下来只需要通过递归来一层层推回去即可。

接下来附上代码:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <algorithm>
#define maxnum 2147483647
using namespace std;
int n;
typedef struct
{
    double x,y;
} atp;

atp a[100005];

double mini(double x,double y)
{
    return x<y?x:y;
}

bool cmp1(atp a,atp b)
{
    return a.x<b.x;
}

bool cmp2(atp a,atp b)
{
    return a.y<b.y;
}

double work(int f,int r)
{
    int mid,i,j,ff,rr;
    double ans;   //注意类型1.不要使用int类型 2.float精度不够,话说float精度有这么低么。。。郁闷
    if(f==r) return maxnum;
    else
    {
        if((r-f)==1) return ((a[r].x-a[f].x)*(a[r].x-a[f].x)+(a[r].y-a[f].y)*(a[r].y-a[f].y));  //递归的结束条件是要最先考虑的
        else
        {
            mid=(f+r)/2;
            ans=mini(work(f,mid),work(mid+1,r));
            //找到y符合的区间
            for(i=mid;i>=f;i--)
                if(a[mid].y-a[i].y>ans)
                    break;
            for(j=mid+1;j<=r;j++)
                if(a[j].y-a[mid].y>ans)
                    break;
            ff=i+1;rr=j-1;
            sort(a+ff,a+rr+1,cmp1);  //对y符合的区间以x为关键字进行排序
            //筛选符合要求的x区间,并对其进行最终确认
            for(i=ff;i<rr;i++)
            {
                for(j=i+1;j<=rr && a[j].x-a[i].x<ans;j++)
                {
                    ans=mini(ans,(a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y));
                }
            }
            return ans;
        }

    }
}

int main()
{
    int i;
    double ans;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0) break;
        for(i=1;i<=n;i++)
            scanf("%lf%lf",&a[i].x,&a[i].y);
        sort(a+1,a+n+1,cmp2); //c++ sort 的规则要注意
        ans=work(1,n);
        printf("%.2lf\n",sqrt(ans)/2);
    }
    return 0;
}


       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值