HDU 5992 Finding Hotels KDtree

该博客介绍了如何利用KD树算法解决一个关于找到离每个游客最近且游客能负担得起的旅馆的问题。博客指出,虽然题目涉及到价格限制和旅馆输入顺序的考虑,但核心是实现一个多维查询的KD树。博主分享了自己的学习过程,并提供了AC代码作为模板。

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

    题意是说有T组样例输入,然后输入N和M,N是旅馆的个数,M是游客的个数。假设旅馆中来多少人都能住开,接下来N行分别是旅馆的位置x,y和旅馆的价格c,然后M行分别是这些人现在的位置和他们所带的钱。要求输出离每个人最近且这个人能付起这个旅馆房费的旅馆(只要付得起就行,不用考虑价格高低)。如果有很多旅馆同时满足要求,就输出样例中先输入进去的那一个旅馆。

    这个题几乎是KD树的一个模板题,只是多加了价格的限制以及最后处理一下旅馆顺序进行输出。抛开这些就是一个求最近距离的KD树问题。也是之前从来没见过这个算法,看了一下,其实和线段树也差不多,只是在处理上这是一个多维的二叉搜索树,进行多维查询,具体的就不多说了,因为对于KD树我自己其实也没太深入看懂= =临近区域赛,从网上找了份题解先看懂了,记录一下为比赛准备模板~

    下面AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<climits>
using namespace std;
long long n,m;
long long cmp_d;
long long root;

struct node
{
    long long l,r;
    long long d[3],Max[3],Min[3];
    long long id;
};

node a[200005];

bool cmp(node a,node b)
{
    return a.d[cmp_d]<b.d[cmp_d];
}

int up(long long p,long long k)
{
    int i;
    for(i=0;i<3;i++)
    {
        a[p].Min[i]=min(a[p].Min[i],a[k].Min[i]);
        a[p].Max[i]=max(a[p].Max[i],a[k].Max[i]);
    }
    return 0;
}

long long build(long long l,long long r,long long D)
{
    int i;
    cmp_d=D;
    long long mid=(l+r)/2;
    nth_element(a+1+l,a+1+mid,a+1+r,cmp);
    for(i=0;i<3;i++)
        a[mid].Min[i]=a[mid].Max[i]=a[mid].d[i];
    if(l!=mid)
        a[mid].l=build(l,mid-1,(D+1)%3);
    else
        a[mid].l=0;
    if(r!=mid)
        a[mid].r=build(mid+1,r,(D+1)%3);
    else
        a[mid].r=0;
    if(a[mid].r)
        up(mid,a[mid].r);
    if(a[mid].l)
        up(mid,a[mid].l);
    return mid;
}

long long x,y,z;
long long jl,ans;

long long getdis(long long p)
{
    long long res=0;
    if(z<a[p].Min[2])
        return LLONG_MAX;
    if(x>a[p].Max[0])
        res+=(x-a[p].Max[0])*(x-a[p].Max[0]);
    if(x<a[p].Min[0])
        res+=(a[p].Min[0]-x)*(a[p].Min[0]-x);
    if(y>a[p].Max[1])
        res+=(y-a[p].Max[1])*(y-a[p].Max[1]);
    if(y<a[p].Min[1])
        res+=(a[p].Min[1]-y)*(a[p].Min[1]-y);
    return res;
}

int ask(long long p)
{
    long long d0=0;
    long long dl,dr;
    if(a[p].d[2]>z)
        d0+=LLONG_MAX;
    else
    {
        d0+=(a[p].d[0]-x)*(a[p].d[0]-x)+(a[p].d[1]-y)*(a[p].d[1]-y);
        if(d0<jl)
        {
            ans=p;
            jl=d0;
        }
        else if(d0==jl)
        {
            if(a[p].id<a[ans].id)
            {
                ans=p;
            }
        }
    }
    if(a[p].l)
        dl=getdis(a[p].l);
    else
        dl=LLONG_MAX;
    if(a[p].r)
        dr=getdis(a[p].r);
    else
        dr=LLONG_MAX;
    if(dl<dr)
    {
        if(dl<=jl)
            ask(a[p].l);
        if(dr<=jl)
            ask(a[p].r);
    }
    else
    {
        if(dr<=jl)
            ask(a[p].r);
        if(dl<=jl)
            ask(a[p].l);
    }
    return 0;
}

int main()
{
    int T;
    int i,j;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld",&n,&m);
        for(i=1;i<=n;i++)
        {
            for(j=0;j<3;j++)
            {
                scanf("%lld",&a[i].d[j]);
            }
            a[i].l=a[i].r=0;
            a[i].id=i;
        }
        root=build(1,n,0);
        for(i=1;i<=m;i++)
        {
            scanf("%lld%lld%lld",&x,&y,&z);
            jl=LLONG_MAX;
            ans=-1;
            ask(root);
            cout<<a[ans].d[0]<<" "<<a[ans].d[1]<<" "<<a[ans].d[2]<<endl;
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值