题意: 给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;
}