您的位置:首页 > 其它

2017.3.25 SJY摆石子 思考记录

2017-03-25 20:19 323 查看
        终于找到了一个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
; //对于d集的:只有xy有用,指每个点读入的坐标

bool cmp(dian x,dian y)
{ //cout<<xycmp<<" ";
return (x.xy[xycmp]<y.xy[xycmp]); //比较的是这一次比较的坐标的大小
}

struct kdtree
{
int xia
[2]; //指向这个节点的两个儿子
dian s
,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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: