【 HDU2966 】In case of failure(KD-Tree)

BUPT2017 wintertraining(15) #5E

HDU - 2966

题意

给平面直角坐标系下的n个点的坐标,求离每个点和它最近点的距离的平方。\(2 \le n \le 10^5\)

题解

这题是裸的kdtree。
kdtree就是k-dimension tree的缩写,是一种分割k维数据空间的数据结构,可用来多维空间数据的范围搜索和最近邻搜索。
这题只是求2维的最近的点,代码比较短。以下是我对算法的理解:

算法过程:
建树:
  1. 分割区间:区间[L,R],m为中点。当前分割方式为d,d=0,则纵向分割,按x坐标排序,否则横向分割,按y坐标排序。将中位数放在m位置,小于中位数的放在m前面(不必有序),大于中位数的放在后面(不必有序),这一步可以调用stl的nth_element函数。

  2. 递归分割子区间:分割左右子树时d为d^1。

查询:
  1. 二叉树查找:用a[m]点到p的距离md来更新一下最小距离nd。p.x[i]为第i维的坐标,如果p.x[d]>a[m].x[d],则查询右子树,否则左子树,直到叶子节点。

  2. 回溯:因为最近点未必在p所在的子空间里,因此判断一下p到a[m]所在的分割线的直线距离是否小于当前最小距离nd,若是,则另一半子空间可能存在点距离p更近,于是进入查找。

其它就是,注意最近点不能是本身,nd初始值为0,nd==0则一定要更新,否则若md==0则不能拿来更新nd。

代码

#include<cstdio>
#include<algorithm>
#define N 100005
#define ll long long
using namespace std;
struct point{
    int x[2];
}a[N],b[N];
ll nd;
int t,n,now;
bool cmp(point a,point b){return a.x[now] < b.x[now];}
ll sqr(int x){return (ll)x*x;}
ll dis(point a,point b){return sqr(a.x[0]-b.x[0])+sqr(a.x[1]-b.x[1]);}

void build(int l,int r,int d){
    if(l>=r) return;
    int m=l+r>>1;
    now=d;
    nth_element(a+l,a+m,a+r,cmp);
    build(l,m,d^1);
    build(m+1,r,d^1);
}
void query(int l,int r,int d,point p){
    if(l>=r)return;
    int m=l+r>>1,sl=l,sr=m;
    ll md=dis(a[m],p);
    if(nd==0||md&&nd>md)nd=md;
    if(p.x[d]>a[m].x[d])sl=m+1,sr=r;
    query(sl,sr,d^1,p);
    if(nd>sqr(a[m].x[d]-p.x[d]))
        query(l+m+1-sl,m+r-sr,d^1,p);
}
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%d%d",a[i].x,a[i].x+1),b[i]=a[i];
        build(0,n,0);
        for(int i=0;i<n;i++)
            nd=0,query(0,n,0,b[i]),printf("%lld\n",nd);
    }
    return 0;
}

转载于:https://www.cnblogs.com/flipped/p/6493340.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值