HDU 5992 Finding Hotels(KD树)题解

题意:n家旅店,每个旅店都有坐标x,y,每晚价钱z,m个客人,坐标x,y,钱c,问你每个客人最近且能住进去(非花最少钱)的旅店,一样近的选排名靠前的。

思路:KD树模板题

代码:

#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
typedef long long ll;
const int maxn = 200000 + 10;
const int seed = 131;
const ll MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
using namespace std;

#define lson rt << 1
#define rson rt << 1 | 1
#define Pair pair<ll, Node>

int k, idx;   //维数k

struct Node
{
    int feature[3];     //定义属性数组
    int id;
    bool operator < (const Node &u) const
    {
        return feature[idx] < u.feature[idx];
    }
}_data[maxn], p;   //_data[]数组代表输入的数据
Node data[4 * maxn];    //data[]数组代表K-D树的所有节点数据
int flag[4 * maxn];      //用于标记某个节点是否存在,1表示存在,-1表示不存在

priority_queue<Pair> Q;     //队列Q用于存放离p最近的m个数据
ll Sqrt(ll a, ll b){    //欧几里得距离平方
    return (a - b) * 1LL * (a - b);
}

//建树步骤,参数dept代表树的深度
void Build(int l, int r, int rt, int dept)
{
    if(l > r) return;
    flag[rt] = 1;                   //表示编号为rt的节点存在
    flag[lson] = flag[rson] = -1;   //当前节点的孩子暂时标记不存在
    idx = dept % k;                 //按照编号为idx的属性进行划分
    int mid = (l + r) >> 1;
    nth_element(_data + l, _data + mid, _data + r + 1);   //nth_element()为STL中的函数 algorithm
    data[rt] = _data[mid];
    Build(l, mid - 1, lson, dept + 1);  //递归左子树
    Build(mid + 1, r, rson, dept + 1);  //递归右子树
}

//查询函数,寻找离p最近的m个特征属性
void Query(Node p, int m, int rt, int dept)
{
    if(flag[rt] == -1) return;   //不存在的节点不遍历
    Pair cur(0, data[rt]);       //获取当前节点的数据和到p的距离
    for(int i = 0; i < k; i++)   //欧几里得距离的平方
        cur.first += Sqrt((ll)data[rt].feature[i], (ll)p.feature[i]);
    int dim = dept % k;          //跟建树一样,这样能保证相同节点的dim值不变
    bool fg = 0;                 //用于标记是否需要遍历右子树
    int x = lson;
    int y = rson;
    if(p.feature[dim] >= data[rt].feature[dim]) //数据p的第dim个特征值大于等于当前的数据,则需要进入右子树
        swap(x, y);
    if(~flag[x]) Query(p, m, x, dept + 1);      //如果节点x存在,则进入子树继续遍历

    //以下是回溯过程,维护一个优先队列
    if(Q.size() < m)   //如果队列没有满,则继续放入
    {
        if(cur.second.feature[2] <= p.feature[2]) Q.push(cur);
        fg = 1;
    }
    else
    {
        if(cur.first < Q.top().first && cur.second.feature[2] <= p.feature[2])  //如果找到更小的距离,则用于替换队列Q中最大的距离的数据
        {
            Q.pop();
            Q.push(cur);
        }
        else if(cur.first == Q.top().first && cur.second.id < Q.top().second.id && cur.second.feature[2] <= p.feature[2]){
            Q.pop();
            Q.push(cur);
        }
        if(Sqrt((ll)p.feature[dim], (ll)data[rt].feature[dim]) < Q.top().first)
        {
            fg = 1;
        }
    }
    if(~flag[y] && fg)
        Query(p, m, y, dept + 1);
}
int main(){
    int T, n, m;
    k = 2;
    scanf("%d", &T);
    while(T--){
        scanf("%d%d", &n, &m);
        for(int i = 0; i < n; i++){
            for(int j = 0; j < 3; j++)
                scanf("%d", &_data[i].feature[j]);
            _data[i].id = i;
        }
        Build(0, n - 1, 1, 0);
        while(m--){
            while(!Q.empty()) Q.pop();
            for(int i = 0; i < 3; i++)
                scanf("%d", &p.feature[i]);
            Query(p, 1, 1, 0);
            p = Q.top().second;
            printf("%d %d %d\n", p.feature[0], p.feature[1], p.feature[2]);
        }
    }
    return 0;
}

模板:

#define lson rt << 1
#define rson rt << 1 | 1
#define Pair pair<ll, Node>

int k, idx;   //维数k

struct Node
{
    int feature[3];     //定义属性数组
    int id;
    bool operator < (const Node &u) const
    {
        return feature[idx] < u.feature[idx];
    }
}_data[maxn], p;   //_data[]数组代表输入的数据
Node data[4 * maxn];    //data[]数组代表K-D树的所有节点数据
int flag[4 * maxn];      //用于标记某个节点是否存在,1表示存在,-1表示不存在

priority_queue<Pair> Q;     //队列Q用于存放离p最近的m个数据
ll Sqrt(ll a, ll b){    //欧几里得距离平方
    return (a - b) * 1LL * (a - b);
}

//建树步骤,参数dept代表树的深度
void Build(int l, int r, int rt, int dept)
{
    if(l > r) return;
    flag[rt] = 1;                   //表示编号为rt的节点存在
    flag[lson] = flag[rson] = -1;   //当前节点的孩子暂时标记不存在
    idx = dept % k;                 //按照编号为idx的属性进行划分
    int mid = (l + r) >> 1;
    nth_element(_data + l, _data + mid, _data + r + 1);   //nth_element()为STL中的函数 algorithm
    data[rt] = _data[mid];
    Build(l, mid - 1, lson, dept + 1);  //递归左子树
    Build(mid + 1, r, rson, dept + 1);  //递归右子树
}

//查询函数,寻找离p最近的m个特征属性
void Query(Node p, int m, int rt, int dept)
{
    if(flag[rt] == -1) return;   //不存在的节点不遍历
    Pair cur(0, data[rt]);       //获取当前节点的数据和到p的距离
    for(int i = 0; i < k; i++)   //欧几里得距离的平方
        cur.first += Sqrt((ll)data[rt].feature[i], (ll)p.feature[i]);
    int dim = dept % k;          //跟建树一样,这样能保证相同节点的dim值不变
    bool fg = 0;                 //用于标记是否需要遍历右子树
    int x = lson;
    int y = rson;
    if(p.feature[dim] >= data[rt].feature[dim]) //数据p的第dim个特征值大于等于当前的数据,则需要进入右子树
        swap(x, y);
    if(~flag[x]) Query(p, m, x, dept + 1);      //如果节点x存在,则进入子树继续遍历

    if(Q.size() < m)   //如果队列没有满,则继续放入
    {                  //注意,这里必须让fg=1,以后改时注意
        Q.push(cur);
        fg = 1;
    }
    else
    {
        if(cur.first < Q.top().first)  //如果找到更小的距离,则用于替换队列Q中最大的距离的数据
        {
            Q.pop();
            Q.push(cur);
        }
        if(Sqrt((ll)p.feature[dim], (ll)data[rt].feature[dim]) < Q.top().first)
        {
            fg = 1;
        }
    }
    if(~flag[y] && fg)
        Query(p, m, y, dept + 1);
}

原文地址:https://www.cnblogs.com/KirinSB/p/9763790.html

时间: 2024-08-30 05:18:14

HDU 5992 Finding Hotels(KD树)题解的相关文章

K-D树问题 HDU 4347

K-D树可以看看这个博客写的真心不错!这里存个版 http://blog.csdn.net/zhjchengfeng5/article/details/7855241 HDU 4349 #include <map> //KD树学习http://blog.csdn.net/zhjchengfeng5/article/details/7855241 #include <set> #include <list> #include <cmath> #include

2016 ICPC青岛站---k题 Finding Hotels(K-D树)

题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=5992 Problem Description There are N hotels all over the world. Each hotel has a location and a price. M guests want to find a hotel with an acceptable price and a minimum distance from their locations.

hdu 4347 The Closest M Points (kd树)

hdu 4347 题意: 求k维空间中离所给点最近的m个点,并按顺序输出  . 解法: kd树模板题 . 不懂kd树的可以先看看这个 . 不多说,上代码 . 1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cmath> 5 #include <cstring> 6 #include <queue> 7 #include <s

HDU 3911 Black And White 分段树 题解

Problem Description There are a bunch of stones on the beach; Stone color is white or black. Little Sheep has a magic brush, she can change the color of a continuous stone, black to white, white to black. Little Sheep like black very much, so she wan

bzoj 3053 HDU 4347 : The Closest M Points kd树

bzoj 3053 HDU 4347 : The Closest M Points  kd树 题目大意:求k维空间内某点的前k近的点. 就是一般的kd树,根据实测发现,kd树的两种建树方式,即按照方差较大的维度分开(建树常数大)或者每一位轮换分割(询问常数大),后者更快也更好些,以后就果断写第二种了. #include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using

数据结构(KD树):HDU 4347 The Closest M Points

The Closest M Points Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 98304/98304 K (Java/Others)Total Submission(s): 3285    Accepted Submission(s): 1201 Problem Description The course of Software Design and Development Practice is objection

hdu 2795 Billboard(线段树)

Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 10890    Accepted Submission(s): 4827 Problem Description At the entrance to the university, there is a huge rectangular billboard of

HDU 3954 Level up 线段树

---NotOnlySuccess 出的题--- 看了题之后觉得和HDU 4027有点像,给的K很小,只有10,目测只要有人升级的时候直接更新到叶子节点就ok了.不过不同于HDU 4027 的是,那题每一次更新都相当于这题的一次升级操作,这题里面可能会出现一次操作之后没有升级和出现升级两种情况,一时半会没了思路. 无奈去搜题解,发现我只要维护一个区间当中距离升级最近的人所需要的基础升级经验,即不算等级加成的裸的升级经验,如果在一次涨经验之后,出现当前区间当中有人会升级,直接将每一个要升级的人更新

HDU 1247 Hat’s Words Trie题解

使用Trie的insert函数,主要是要灵活修改search函数,使得其可以快速搜索hat word. 思路: 1 先搜索一个word的前缀有没有在Trie树中找到,如果找到,保存这个Node,方便下面继续搜索, 就搜索余下的能不能是Trie树上的一个单词,如果找到,那么就是hat word了. 2 如果没找到,那么就沿着刚刚搜索到的前缀单词的节点继续往下搜,这样就可以加速程序,不用每次重复搜索. 3 如果沿着Trie树已经走到word的叶子节点了,那么就结束这个word的搜索了. 实现好这些思