zoukankan      html  css  js  c++  java
  • kdTree 【专题@AbandonZHANG】

    刚开始学习,介绍先搁着~等理解透彻了再来写~~~   我是学习的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 In case of failure (最*邻,模板~)   查找*面点最*点的距离(此题中是距离的*方)
    /*
        HDOJ 2966
        KD-Tree模板
    */
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #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);
        }
    };
    
      HDOJ 4347 The Closest M Points (Q*邻)   与上题不同的是,一是k维(这个好处理~),二是求最*的m个点而不单是最*点了。这个也好处理~递归查找时处理的时候采取如下策略:如果当前找到的点小于k个,那么两个区间都要处理。。否则根据当前找到的第k个点决定是否去另外一个区间,如果目标点到分界线的距离大于等于已经找到的第k远的点,那么就不用查找另一个分界了。。。更新答案可以用一个大小为k的堆去维护(一个最大堆,一旦超过k个点就把最大的扔掉)。。。
    #include 
     #include 
     #include 
     #include 
     #include 
     #include 
     #include 
     #include 
     #include 
     #include 
     #include 
     #include 
     #define MID(x,y) ( (x + y)>>1 )
     
     using namespace std;
     typedef long long LL;
     
     //KD-Tree模板
     const int N=50005;
     
     struct Point
     {
         int x[5];
         LL dis;
         Point()
         {
             for (int i = 0; i < 5; i++)
                 x[i] = 0;
             dis = 9223372036854775807LL;
         }
         friend bool operator < (const Point &a, const Point &b)
         {
             return a.dis < b.dis;
         }
     };
     priority_queue  > res;
     
     LL dist2(const Point &a, const Point &b, int k)            //距离的*方,开根号很耗时,能不开就不开
     {
         LL ans = 0;
         for (int i = 0; i < k; i++)         //一开始这儿写的i < 5,WA了N次。。。原本以为只要初始值设0就无所谓,
             ans += (a.x[i] - b.x[i]) * (a.x[i] - b.x[i]);   //但发现Point有个全局变量,在多case下会出错。。。
         return ans;
     }
     
     int ddiv;
     bool cmpX(const Point &a, const Point &b)
     {
         return a.x[ddiv] < b.x[ddiv];
     }
     
     struct KDTree        //很崇拜这种销魂的建树方法啊~0.0~很抽象很强大------p数组已经代表了KD-Tree了,神马左右子树全省了,OOOOOrrz!!!……
     {
         Point p[N];        //空间内的点
         int Div[N];        //记录区间是按什么方式划分(分割线*行于x轴还是y轴, ==1*行y轴切;==0*行x轴切)
         int k;          //维数
         int m;          //*邻
     
         int getDiv(int l, int r)        //寻找区间跨度最大的划分方式
         {
             map  ms;
             int minx[5],maxx[5];
             for (int i = 0; i < k; i++)
             {
                 ddiv = i;
                 minx[i] = min_element(p + l, p + r + 1, cmpX)->x[i];
                 maxx[i] = max_element(p + l, p + r + 1, cmpX)->x[i];
                 ms[maxx[i] - minx[i]] = i;
             }
             map ::iterator pm = ms.end();
             pm--;
             return pm->second;
         }
     
         void build(int l, int r)            //记得先把p备份一下。
         {
             if (l > r)    return;
             int mid = MID(l,r);
             Div[mid] = getDiv(l,r);
             ddiv = Div[mid];
             nth_element(p + l, p + mid, p + r + 1, cmpX);
             build(l, mid - 1);
             build(mid + 1, r);
         }
     
         void findk(int l, int r, Point a)                //k(m)*邻,查找k*点的*方距离
         {
             if (l > r)    return;
             int mid = MID(l,r);
             LL dist = dist2(a, p[mid], k);
             if (dist >= 0)
             {
                 p[mid].dis = dist;
                 res.push(p[mid]);
                 while ((int)res.size() > m)
                     res.pop();
             }
             LL d = a.x[Div[mid]] - p[mid].x[Div[mid]];
             int l1, l2, r1, r2;
             l1 = l , l2 = mid + 1;
             r1 = mid - 1, r2 = r;
             if (d > 0)
                 swap(l1, l2), swap(r1, r2);
             findk(l1, r1, a);
             if ((int)res.size() < m || d*d < res.top().dis )
                 findk(l2, r2, a);
         }
     };
     
     Point pp[N];
     KDTree kd;
     
     int main()
     {
     //    freopen("test.txt","r+",stdin);
     //    freopen("ans.txt","w+",stdout);
     
         int n;
         while(scanf("%d%d", &n, &kd.k)!=EOF)
         {
             for (int i = 0; i < n; i++)
                 for (int j = 0; j < kd.k; j++)
                 {
                     scanf("%d", &pp[i].x[j]);
                     kd.p[i] = pp[i];
                 }
             kd.build(0, n - 1);
             int t;
             scanf("%d", &t);
             while(t--)
             {
                 Point a;
                 for (int i = 0; i < kd.k; i++)
                     scanf("%d", &a.x[i]);
                 scanf("%d", &kd.m);
                 kd.findk(0, n - 1, a);
                 printf("the closest %d points are:\n", kd.m);
                 Point ans[11];
                 for (int i = 0; !res.empty(); i++)
                 {
                     ans[i] = res.top();
                     res.pop();
                 }
                 for (int i = kd.m - 1; i >= 0; i--)
                 {
                     for (int j = 0; j < kd.k - 1; j++)
                         printf("%d ", ans[i].x[j]);
                     printf("%d\n", ans[i].x[kd.k - 1]);
                 }
             }
     
         }
         return 0;
     }
    
     
    举杯独醉,饮罢飞雪,茫然又一年岁。 ------AbandonZHANG
  • 相关阅读:
    不上大学遗撼,上过大学后悔
    消息队列(MSMQ)实现多服务器应用程序之间消息实时交互
    抓取网页源代码
    用asp.net显示在线登陆人数及位置
    原版对XML文档的读写
    C#.NET实现经典排序算法
    深入剖析C#继承机制
    ASP.NET长文章分页
    人民币小写金额转化成大写金额
    GridView和DataFormatString
  • 原文地址:https://www.cnblogs.com/AbandonZHANG/p/4114170.html
Copyright © 2011-2022 走看看