zoukankan      html  css  js  c++  java
  • kd树 【@Abandon】

    刚开始学习,介绍先搁着~等理解透彻了再来写~~~

     

    我是学习的mzry1992(UESTC_Izayoi ~)------http://www.mzry1992.com/blog/miao/kd%E6%A0%91.html

    先去看mzry1992大牛博客里的讲解吧。。。

    再附两篇论文:(看英文看得好爽。。。~@.@)

    《An intoductory tutorial on kd-trees》  ★(里面就介绍了kd-tree和nearest neighbour algorithm(最*邻算法)、Q nearest neighbour(Q*邻)

    《Range Searching Using Kd-Tree.》

      

    模板:

    kd-tree模板
    /*
        HDOJ 2966
        KD-Tree模板
    */
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cmath>
    #include <vector>
    #include <stack>
    #include <queue>
    #include <map>
    #include <algorithm>
    #include <string>
    #include <cstring>
    #define MID(x, y) ( (x + y)>>1 )
    
    using namespace std;
    typedef long long LL;
    
    //KD-Tree模板
    const int N=100005;
    LL res;
    struct Point
    {
        int x, y;        //点是二维的,此时是2D-Tree
    };
    
    LL dist2(const Point &a, const Point &b)            //距离的*方
    {
        return LL(a.x - b.x) * LL(a.x - b.x) + LL(a.y - b.y) * LL(a.y - b.y);
    }
    
    bool cmpX(const Point &a, const Point &b)
    {
        return a.x < b.x;
    }
    bool cmpY(const Point &a, const Point &b)
    {
        return a.y < b.y;
    }
    
    struct KDTree        //很崇拜这种销魂的建树方法啊~0.0~很抽象很强大------p数组已经代表了KD-Tree了,神马左右子树全省了,OOOOOrrz!!!……
    {
        Point p[N];        //空间内的点
        int Div[N];        //记录区间是按什么方式划分(分割线*行于x轴还是y轴, ==1*行y轴切;==0*行x轴切)
    
        void build(int l, int r)            //记得先把p备份一下。
        {
            if (l > r)    return;
            int mid=MID(l, r);
            int minX, minY, maxX, maxY;
            minX = min_element(p + l, p + r + 1, cmpX)->x;
            minY = min_element(p + l, p + r + 1, cmpY)->y;
            maxX = max_element(p + l, p + r + 1, cmpX)->x;
            maxY = max_element(p + l, p + r + 1, cmpY)->y;
            Div[mid] = (maxX - minX >= maxY - minY);
            nth_element(p + l, p + mid, p + r + 1, Div[mid] ? cmpX : cmpY);
            build(l, mid - 1);
            build(mid+1, r);
        }
    
        void find(int l, int r, Point a)                //查找最*点的*方距离
        {
            if (l > r)    return;
            int mid = MID(l, r);
            LL dist = dist2(a, p[mid]);
            if (dist > 0)   //如果有重点不能这么判断
                res = min(res, dist);
            LL d = Div[mid] ? (a.x - p[mid].x) : (a.y - p[mid].y);
            int l1, l2, r1, r2;
            l1 = l , l2 = mid + 1;
            r1 = mid - 1, r2 = r;
            if (d > 0)
                swap(l1, l2), swap(r1, r2);
            find(l1, r1, a);
            if (d * d < res)
                find(l2, r2, a);
        }
    };

     

    kd-tree入门题:

     

      HDOJ 2966 In case of failure (最*邻,直接套模板~)

      查找*面点最*点的距离(此题中是距离的*方)

    HDOJ 2966
      1 /*
      2     HDOJ 2966
      3     KD-Tree模板
      4 */
      5 #include <iostream>
      6 #include <cstdio>
      7 #include <cstdlib>
      8 #include <cmath>
      9 #include <vector>
     10 #include <stack>
     11 #include <queue>
     12 #include <map>
     13 #include <algorithm>
     14 #include <string>
     15 #include <cstring>
     16 #define MID(x, y) ( (x + y)>>1 )
     17 
     18 using namespace std;
     19 typedef long long LL;
     20 
     21 //KD-Tree模板
     22 const int N=100005;
     23 LL res;
     24 struct Point
     25 {
     26     int x, y;        //点是二维的,此时是2D-Tree
     27 };
     28 
     29 LL dist2(const Point &a, const Point &b)            //距离的*方
     30 {
     31     return LL(a.x - b.x) * LL(a.x - b.x) + LL(a.y - b.y) * LL(a.y - b.y);
     32 }
     33 
     34 bool cmpX(const Point &a, const Point &b)
     35 {
     36     return a.x < b.x;
     37 }
     38 bool cmpY(const Point &a, const Point &b)
     39 {
     40     return a.y < b.y;
     41 }
     42 
     43 struct KDTree        //很崇拜这种销魂的建树方法啊~0.0~很抽象很强大------p数组已经代表了KD-Tree了,神马左右子树全省了,OOOOOrrz!!!……
     44 {
     45     Point p[N];        //空间内的点
     46     int Div[N];        //记录区间是按什么方式划分(分割线*行于x轴还是y轴, ==1*行y轴切;==0*行x轴切)
     47 
     48     void build(int l, int r)            //记得先把p备份一下。
     49     {
     50         if (l > r)    return;
     51         int mid=MID(l, r);
     52         int minX, minY, maxX, maxY;
     53         minX = min_element(p + l, p + r + 1, cmpX)->x;
     54         minY = min_element(p + l, p + r + 1, cmpY)->y;
     55         maxX = max_element(p + l, p + r + 1, cmpX)->x;
     56         maxY = max_element(p + l, p + r + 1, cmpY)->y;
     57         Div[mid] = (maxX - minX >= maxY - minY);
     58         nth_element(p + l, p + mid, p + r + 1, Div[mid] ? cmpX : cmpY);
     59         build(l, mid - 1);
     60         build(mid+1, r);
     61     }
     62 
     63     void find(int l, int r, Point a)                //查找最*点的*方距离
     64     {
     65         if (l > r)    return;
     66         int mid = MID(l, r);
     67         LL dist = dist2(a, p[mid]);
     68         if (dist > 0)   //如果有重点不能这么判断
     69             res = min(res, dist);
     70         LL d = Div[mid] ? (a.x - p[mid].x) : (a.y - p[mid].y);
     71         int l1, l2, r1, r2;
     72         l1 = l , l2 = mid + 1;
     73         r1 = mid - 1, r2 = r;
     74         if (d > 0)
     75             swap(l1, l2), swap(r1, r2);
     76         find(l1, r1, a);
     77         if (d * d < res)
     78             find(l2, r2, a);
     79     }
     80 };
     81 
     82 Point pp[N];
     83 KDTree kd;
     84 
     85 int main()
     86 {
     87     //freopen("test.txt","r+",stdin);
     88     int t;
     89     scanf("%d", &t);
     90     while(t--)
     91     {
     92         int n;
     93         scanf("%d", &n);
     94         for (int i = 0; i < n; i++)
     95         {
     96             scanf("%d%d", &pp[i].x, &pp[i].y);
     97             kd.p[i] = pp[i];
     98         }
     99 
    100         kd.build(0, n-1);
    101         for (int i = 0; i < n; i++)
    102         {
    103             res = 9223372036854775807LL;
    104             kd.find(0, n - 1, pp[i]);
    105             printf("%I64d\n", res);
    106         }
    107     }
    108     return 0;
    109 }

      HDOJ 4347 The Closest M Points (Q*邻)

      与上题不同的是,一是k维(这个好处理~),二是求最*的m个点而不单是最*点了。这个也好处理~递归查找时处理的时候采取如下策略:如果当前找到的点小于k个,那么两个区间都要处理。。否则根据当前找到的第k个点决定是否去另外一个区间,如果目标点到分界线的距离大于等于已经找到的第k远的点,那么就不用查找另一个分界了。。。更新答案可以用一个大小为k的堆去维护(一个最大堆,一旦超过k个点就把最大的扔掉)。。。

      2s,速度还是不错的^_^~

    6811512 2012-09-21 16:58:20 Accepted 4347 2421MS 3848K 4152 B G++ AbandonZHANG
    HDOJ 4347
      1 /*
      2     HDOJ 2966
      3     KD-Tree模板
      4 */
      5 #include <iostream>
      6 #include <cstdio>
      7 #include <cstdlib>
      8 #include <cmath>
      9 #include <vector>
     10 #include <stack>
     11 #include <queue>
     12 #include <set>
     13 #include <map>
     14 #include <algorithm>
     15 #include <string>
     16 #include <cstring>
     17 #define MID(x,y) ( (x + y)>>1 )
     18 
     19 using namespace std;
     20 typedef long long LL;
     21 
     22 //KD-Tree模板
     23 const int N=50005;
     24 
     25 struct Point
     26 {
     27     int x[5];
     28     LL dis;
     29     Point()
     30     {
     31         for (int i = 0; i < 5; i++)
     32             x[i] = 0;
     33         dis = 9223372036854775807LL;
     34     }
     35     friend bool operator < (const Point &a, const Point &b)
     36     {
     37         return a.dis < b.dis;
     38     }
     39 };
     40 priority_queue <Point, vector<Point> > res;
     41 
     42 LL dist2(const Point &a, const Point &b, int k)            //距离的*方,开根号很耗时,能不开就不开
     43 {
     44     LL ans = 0;
     45     for (int i = 0; i < k; i++)         //一开始这儿写的i < 5,WA了N次。。。原本以为只要初始值设0就无所谓,
     46         ans += (a.x[i] - b.x[i]) * (a.x[i] - b.x[i]);   //但发现Point有个全局变量,在多case下会出错。。。
     47     return ans;
     48 }
     49 
     50 int ddiv;
     51 bool cmpX(const Point &a, const Point &b)
     52 {
     53     return a.x[ddiv] < b.x[ddiv];
     54 }
     55 
     56 struct KDTree        //很崇拜这种销魂的建树方法啊~0.0~很抽象很强大------p数组已经代表了KD-Tree了,神马左右子树全省了,OOOOOrrz!!!……
     57 {
     58     Point p[N];        //空间内的点
     59     int Div[N];        //记录区间是按什么方式划分(分割线*行于x轴还是y轴, ==1*行y轴切;==0*行x轴切)
     60     int k;          //维数
     61     int m;          //*邻
     62 
     63     int getDiv(int l, int r)        //寻找区间跨度最大的划分方式
     64     {
     65         map <int, int> ms;
     66         int minx[5],maxx[5];
     67         for (int i = 0; i < k; i++)
     68         {
     69             ddiv = i;
     70             minx[i] = min_element(p + l, p + r + 1, cmpX)->x[i];
     71             maxx[i] = max_element(p + l, p + r + 1, cmpX)->x[i];
     72             ms[maxx[i] - minx[i]] = i;
     73         }
     74         map <int ,int>::iterator pm = ms.end();
     75         pm--;
     76         return pm->second;
     77     }
     78 
     79     void build(int l, int r)            //记得先把p备份一下。
     80     {
     81         if (l > r)    return;
     82         int mid = MID(l,r);
     83         Div[mid] = getDiv(l,r);
     84         ddiv = Div[mid];
     85         nth_element(p + l, p + mid, p + r + 1, cmpX);
     86         build(l, mid - 1);
     87         build(mid + 1, r);
     88     }
     89 
     90     void findk(int l, int r, Point a)                //k(m)*邻,查找k*点的*方距离
     91     {
     92         if (l > r)    return;
     93         int mid = MID(l,r);
     94         LL dist = dist2(a, p[mid], k);
     95         if (dist >= 0)
     96         {
     97             p[mid].dis = dist;
     98             res.push(p[mid]);
     99             while ((int)res.size() > m)
    100                 res.pop();
    101         }
    102         LL d = a.x[Div[mid]] - p[mid].x[Div[mid]];
    103         int l1, l2, r1, r2;
    104         l1 = l , l2 = mid + 1;
    105         r1 = mid - 1, r2 = r;
    106         if (d > 0)
    107             swap(l1, l2), swap(r1, r2);
    108         findk(l1, r1, a);
    109         if ((int)res.size() < m || d*d < res.top().dis )
    110             findk(l2, r2, a);
    111     }
    112 };
    113 
    114 Point pp[N];
    115 KDTree kd;
    116 
    117 int main()
    118 {
    119 //    freopen("test.txt","r+",stdin);
    120 //    freopen("ans.txt","w+",stdout);
    121 
    122     int n;
    123     while(scanf("%d%d", &n, &kd.k)!=EOF)
    124     {
    125         for (int i = 0; i < n; i++)
    126             for (int j = 0; j < kd.k; j++)
    127             {
    128                 scanf("%d", &pp[i].x[j]);
    129                 kd.p[i] = pp[i];
    130             }
    131         kd.build(0, n - 1);
    132         int t;
    133         scanf("%d", &t);
    134         while(t--)
    135         {
    136             Point a;
    137             for (int i = 0; i < kd.k; i++)
    138                 scanf("%d", &a.x[i]);
    139             scanf("%d", &kd.m);
    140             kd.findk(0, n - 1, a);
    141             printf("the closest %d points are:\n", kd.m);
    142             Point ans[11];
    143             for (int i = 0; !res.empty(); i++)
    144             {
    145                 ans[i] = res.top();
    146                 res.pop();
    147             }
    148             for (int i = kd.m - 1; i >= 0; i--)
    149             {
    150                 for (int j = 0; j < kd.k - 1; j++)
    151                     printf("%d ", ans[i].x[j]);
    152                 printf("%d\n", ans[i].x[kd.k - 1]);
    153             }
    154         }
    155 
    156     }
    157     return 0;
    158 }

     

    (未完待续。。。)

      

    举杯独醉,饮罢飞雪,茫然又一年岁。 ------AbandonZHANG
  • 相关阅读:
    C语言读写伯克利DB 4
    程序之美(转自知乎)
    C语言读写伯克利DB 3
    ON DUPLICATE KEY UPDATE
    nanomsg:ZeroMQ作者用C语言新写的消息队列库
    新浪研发中心: Berkeley DB 使用经验总结
    [企业开源系列]后起之秀Facebook凭什么挑战互联网霸主Google?
    BZOJ1770:[USACO]lights 燈(高斯消元,DFS)
    BZOJ5293:[BJOI2018]求和(LCA,差分)
    BZOJ5301:[CQOI2018]异或序列(莫队)
  • 原文地址:https://www.cnblogs.com/AbandonZHANG/p/2694395.html
Copyright © 2011-2022 走看看