In case of failure HDU - 2966(kd_tree)

题意: 给n个二维坐标表示n个点,求每一个点到它最近的点的距离
思路:kd_tree模板题,看了题解终于会写模板题了

大致原理: 是基于BST的,只不过BST是一维的,而KD_tree可以划分多维的情况。
但也有一点不同的情况,距离是要考虑多维的,不能简单的通过某一维的划分,就判断出正解,所以我们可以先向最有可能是正解的地方查询,然后回溯判断其他情况,此时由于已经有了一个较优秀的答案就可以减去较多的枝。

实现细节见于代码:

#include <bits/stdc++.h>

using namespace std;
#define ll long long
const int N = 1e5 + 50;

struct Point // 点的坐标
{
	ll x[3];
}p[N], ori[N];

int split[20], cur, dim;
// cur 当前划分的是是第几维  dim 总共是第几维
bool cmp(const Point &a, const Point &b)
{
	return a.x[cur] < b.x[cur];
}
#define lson l, m - 1, depth + 1
#define rson m + 1, r, depth + 1
// 模板函数
template <class T> T sqr(T x) {return x * x;}

const ll inf = 0x7777777777777777ll;
ll dist(const Point &x, const Point &y) // 返回两个点的距离
{
	ll ret = 0;
	for(int i = 0; i < dim; i ++)
	{
		ret += sqr(x.x[i] - y.x[i]);
	}
	return ret? ret:inf;//防止查到的点就是自己即 x == y的情况
}

void build(const int &l, const int &r, const int &depth) // 建树
{
	if(l >= r) return; // 如果l == r, 则说明只有一个点不需要被划分了
	int m = l + r >> 1;
	cur = depth % dim; // 当前划分是第几维
	nth_element(p + l, p + m, p + r + 1, cmp);//划分
	//划分m点, m的左边在第cur维度在m的左边的元素, 反之在其右边
	build(lson);
	build(rson);
}
// 查找离x最近的点
ll Find(const Point &x, const int &l, const &r, const int &depth)
{
	int cur = depth % dim;//第几维
	if(l >= r)
	{
		//如果无法继续查找
		if(l == r) return dist(x, p[l]);
		return inf;
	}
	int m = l + r >> 1;
	ll ret = dist(x, p[m]), tmp; // ret记录与x的距离
	if(x.x[cur] < p[m].x[cur])
	{
		/*如果在当前维度下, x的cur维坐标小于p的那么就查询它的左边
		*/
		tmp = Find(x, lson);
		if(tmp > sqr(x.x[cur] - p[m].x[cur]))//答案也可能在右边,但必须满足当前已经查找到的距离tmp,大于(至少比当前维度的距离)才可能更新最小解,这样减了不少枝
			tmp = min(tmp, Find(x, rson));
	}
	else
	{
		tmp = Find(x, rson);//同理
		if(tmp > sqr(x.x[cur] - p[m].x[cur]))
		{
			tmp = min(tmp, Find(x, lson));
		}
	}
	return min(ret, tmp);
}

int main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	int n, T;
	cin >> T;
	while(T --)
	{
		cin >> n;
		dim = 2;
		for(int i = 0; i < n; i++)
		{
			for(int j = 0; j < 2; j ++)
			{
				cin >> ori[i].x[j];
			}
			p[i] = ori[i]; // 保存点集
		}
		build(0, n - 1, 0);//建立KD_tree,进行划分
		for(int i = 0; i < n; i++)
		{
			cout << Find(ori[i], 0, n - 1, 0) << endl;//查询离第i个点最近的点的距离
		}
	}
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值