zoukankan      html  css  js  c++  java
  • 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
    ", 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);
    }
  • 相关阅读:
    数组类型
    约瑟夫环问题
    const在c和c++中地位不同
    Makefile学习之路——4
    单链表综合操作
    算法初探——大O表示法
    数据结构实用概念
    Makefile学习之路——3
    翻转字符串
    Makefile学习之路——2
  • 原文地址:https://www.cnblogs.com/KirinSB/p/9763790.html
Copyright © 2011-2022 走看看