2017.3.25 SJY摆石子 思考记录

来源:互联网 发布:ios http网络协议 编辑:程序博客网 时间:2024/04/30 02:54

        终于找到了一个kd树的题、、来看看


     k-d树原理还是不难的,就是代码技巧比较多可能可读性比较差

      它就是把一个平面分成:


找最接近的点原理是利用极值矩形:


这个题就是k-d树板子题了、

下面的注释很详细

码:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#define N 1000005#define inf 1000000000 using namespace std;int xycmp,i,n,m,ans,rt,op;struct dian{                              //注意:下标范围为[2]的  0指的是x坐标  1指的是y坐标 int xiao[2],da[2],xy[2];}d[N];                   //对于d集的:只有xy有用,指每个点读入的坐标   bool cmp(dian x,dian y) {   //cout<<xycmp<<" ";return (x.xy[xycmp]<y.xy[xycmp]);                            //比较的是这一次比较的坐标的大小 }struct kdtree{  int xia[N][2];   //指向这个节点的两个儿子   dian s[N],q;       //对于s集的:   xiao  指的是矩形左下角的坐标集    da指的是矩形右上角的点集      xy指的是这个点的坐标 void upd(int now)  //维护矩形 {int l=xia[now][0],r=xia[now][1]; for(int i=0;i<2;i++)//依次循环 x、y坐标 {if(l){s[now].da[i]=max(s[l].da[i],s[now].da[i]);s[now].xiao[i]=min(s[now].xiao[i],s[l].xiao[i]);} //如果有左儿子,就取一遍左儿子x、y  矩形  的   坐标最值  再取最值 if(r){s[now].da[i]=max(s[r].da[i],s[now].da[i]);s[now].xiao[i]=min(s[now].xiao[i],s[r].xiao[i]);}//如果有右儿子,就取一遍右儿子x、y  矩形  的   坐标最值  再取最值     }}void add(int now,dian t)//添加 {for(int i=0;i<2;i++)s[now].xiao[i]=s[now].da[i]=s[now].xy[i]=t.xy[i];//初始化最大、最小、这个s集(kd树上的) 值都是 当前点的x、y坐标 }int dis(int now){int lin=0;    for(int i=0;i<2;i++)lin+=max(s[now].xiao[i]-q.xy[i],0);for(int i=0;i<2;i++)lin+=max(q.xy[i]-s[now].da[i],0);return lin; //这块比较难理解,如果第一个循环>0第二个循环就一定为0  反之亦然   所以它实际上算的是点q到 now点对应的矩形的 曼哈顿距离 }void build(int &k,int l,int r,int xydo)//建kd树   k值要返回,因为没有初值   xydo表示当前比较的什么坐标  0是x 1是y {k=(l+r)>>1;   xycmp=xydo;   //用k作为编号方便(依次标也没人管你,这不是线段树,可以乱),   xycmp是比较的坐标 nth_element(d+l,d+k,d+r+1,cmp);  //这几乎是为kd树量身打造的函数,详情自行百度或见我 stl 算法总结 add(k,d[k]);   //加入这个中间点 if(l<k)build(xia[k][0],l,k-1,xydo^1); //建左子树 if(r>k)build(xia[k][1],k+1,r,xydo^1); //建右子数 upd(k);//维护矩形 }   void ins(int k,int xydo)  //插入点   k是当前已有点的编号         {    if(q.xy[xydo]<s[k].xy[xydo])   //因为小的在左子树  大的在右子树     {    if(xia[k][0])ins(xia[k][0],xydo^1);    else xia[k][0]=++n,add(n,q);//没有的话就找到了归宿   加一个     }else{if(xia[k][1])ins(xia[k][1],xydo^1);     //    else xia[k][1]=++n,add(n,q);                // 同上 }       upd(k);//不要忘了更新 }void zhao(int k){ans=min(ans,(abs(q.xy[0]-s[k].xy[0])+abs(q.xy[1]-s[k].xy[1])));//答案取这个点和要找点的最小距离 int ld=(xia[k][0])?dis(xia[k][0]):inf;//如果在矩形内部就是0,在外部就是最小距离,没有这个子节点就是inf int rd=(xia[k][1])?dis(xia[k][1]):inf;if(ld<rd){if(ld<ans)zhao(xia[k][0]);if(rd<ans)zhao(xia[k][1]);//用访问顺序来优化(优先访问距离短的来更新答案,这样长的就有可能被卡掉) }else{if(rd<ans)zhao(xia[k][1]);if(ld<ans)zhao(xia[k][0]);}}}kd;int main(){scanf("%d%d",&n,&m);for(i=1;i<=n;i++){scanf("%d%d",&d[i].xy[0],&d[i].xy[1]);}kd.build(rt,1,n,0);for(int i=1;i<=m;i++){scanf("%d%d%d",&op,&kd.q.xy[0],&kd.q.xy[1]);if(op==1){kd.ins(rt,0);}else{ans=inf;kd.zhao(rt);printf("%d\n",ans);  }}return 0; } 


0 0
原创粉丝点击