hdu4347 The Closest M Points

http://acm.hdu.edu.cn/showproblem.php?pid=4347

K-D树板题,这题是照着别人的代码抄的,感觉不是很符合我的风格,之后再改。

对于K-D树我的理解感觉还是不太对,之后得多看点博客,再做点题理解才好

首先建树是按照线段树的方式向下建的,然后每一层按照他的深度取不同维度的中位数放在当前点,然后继续向下递归建树。

然后我们再查询的时候,先判断询问的点在当前层应该放到左边还是右边,那么那个子树在该维上的距离就会尽可能小,但是我们不知道在其他维上是否会一样小,所以用一个flag来标记是否需要去另外一棵子树上找,还可以添加答案的,一定要去另一颗上找,否则要看如果当前子树的根节点的对应维度的距离如果是严格小于答案中最大的,那么也需要去另一棵子树上去找找。

所以这就是一个剪枝,看了网上博客说随机数据复杂度是O(logn)的,而对于构造数据会被卡到O(sqrt(n))的水平

#include<bits/stdc++.h>
using namespace std;

const int maxl=5e4+10;

int n,k,m,idx;
struct point
{
	int x[5];
	bool operator < (const point &b)const
	{
		return x[idx]<b.x[idx];
	}
}a[maxl],ans[21];
typedef pair<double,point> pdp;
priority_queue<pdp> q;

struct tree
{
	point p[maxl<<2];
	int son[maxl<<2];
	void build(int l,int r,int u,int dep)
	{
		if(l>r) return;
		son[u]=r-l+1;
		son[u<<1]=son[u<<1|1]=0;
		idx=dep%k;
		int mid=(l+r)>>1;
		nth_element(a+l,a+mid,a+r+1);
		p[u]=a[mid];
		build(l,mid-1,u<<1,dep+1);
		build(mid+1,r,u<<1|1,dep+1);
	}
	void query(point d,int m,int u,int dep)
	{
		pdp nd(0,p[u]);
		for(int i=0;i<k;i++)
			nd.first+=pow(p[u].x[i]-d.x[i],2);
		int id=dep%k,flag=0;
		int x=u<<1,y=u<<1|1;
		if(d.x[id]>=p[u].x[id])
			swap(x,y);
		if(son[x]) 
			query(d,m,x,dep+1);
		if(q.size()<m) 
			q.push(nd),flag=1;
		else
		{
			if(nd.first<q.top().first) 
				q.pop(),q.push(nd);
			if(pow(d.x[id]-p[u].x[id],2)<q.top().first)
				flag=1;
		}
		if(son[y] && flag)
			query(d,m,y,dep+1);
	}
}kd;

inline void prework()
{
	for(int i=1;i<=n;i++)
		for(int j=0;j<k;j++)
			scanf("%d",&a[i].x[j]);
	kd.build(1,n,1,0);
}

inline void mainwork()
{
	int t;point d;
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		for(int j=0;j<k;j++)
			scanf("%d",&d.x[j]);
		scanf("%d",&m);
		while(!q.empty()) 
			q.pop();
		kd.query(d,m,1,0);
		printf("the closest %d points are:\n",m);
		for(int j=1;j<=m;j++)
			ans[j]=q.top().second,q.pop();
		for(int j=m;j>=1;j--)
			for(int ii=0;ii<k;ii++)
				printf("%d%c",ans[j].x[ii],ii==k-1?'\n':' ');
	}
} 

int main()
{
	while(~scanf("%d%d",&n,&k))
	{
		prework();
		mainwork();
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值