HDU 4347 - The Closest M Points - [KDTree模板题]

时间:2022-05-11 00:15:16

本文参考:

https://www.cnblogs.com/GerynOhenz/p/8727415.html

kuangbin的ACM模板(新)


题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4347

Problem Description

The course of Software Design and Development Practice is objectionable. ZLC is facing a serious problem .There are many points in K-dimensional space .Given a point. ZLC need to find out the closest m

points. Euclidean distance is used as the distance metric between two points. The Euclidean distance between points p and q is the length of the line segment connecting them.In Cartesian coordinates, if p =

(p1, p2,..., pn) and q = (q1, q2,..., qn) are two points in Euclidean n-space, then the distance from p to q, or from q to p is given by:

$d\left( {{\bf{p}},{\bf{q}}} \right) = d\left( {{\bf{q}},{\bf{p}}} \right) = \sqrt {\sum\limits_{i = 1}^n {\left( {p_i - q_i } \right)^2 } }$

Can you help him solve this problem?

Input

In the first line of the text file. There are two non-negative integers n and K. They denote respectively: the number of points, 1 <= n <= 50000, and the number of Dimensions, 1 <= K <= 5. In each of the following n lines there is written K integers, representing the coordinates of a point. This followed by a line with one positive integer t, representing the number of queries, 1 <= t <=10000.

Each query contains two lines. The k integers in the first line represent the given point. In the second line, there is one integer m, the number of closest points you should find,1 <= m <=10. The absolute value of all the coordinates will not be more than 10000. There are multiple test cases. Process to end of file.

Output

For each query, output m+1 lines:

The first line saying :”the closest m points are:” where m is the number of the points.

The following m lines representing m points ,in accordance with the order from near to far

It is guaranteed that the answer can only be formed in one ways. The distances from the given point to all the nearest m+1 points are different. That means input like this:

2 2
1 1
3 3
1
2 2
1

will not exist.

Sample Input
3 2
1 1
1 3
3 4
2
2 3
2
2 3
1

Sample Output
the closest 2 points are:
1 3
3 4
the closest 1 points are:
1 3

题意:

给出 $n$ 个 $K$ 维的点,又给出 $t$ 个查询, 每个查询也给出一个 $K$ 维的点,要求查询 $n$ 个点中距离这个点最近的 $M$ 个点。

题解:

KDTree模板题。


以下关于KDTree的小记:

1、简介

KDTree(K-dimensional tree) 是一个支持多维空间的数据结构,主要是将空间内的点进行区域划分,快速维护有关空间点的操作,例如空间的最远(近)点对,区间搜索。

KDTree 的结构与线段树类似,只是线段树是对一维空间的操作,而 KDTree 是多维操作的,这也导致了 KDTree 的灵活性没有线段树高。

树上每个节点所维护的信息:

  1. 左右两个儿子
  2. 该点表示的空间范围(超长方体,当 $K=2$ 时为矩形,$K=3$ 时为长方体)
  3. 中位点(坐标等信息)

2.1、建树

假设我们已经有了一个描述 $K$ 维空间内的点的类Point,且我们已经有Point类型的 $a[0:n-1]$ 数组。

首先因为是空间划分,所以要循环递进地用垂直于各维度轴的超平面(或者线、平面)进行划分。

例如在二维平面上的划分,先用垂直 $x$ 轴的直线进行划分,再用垂直 $y$ 轴的直线进行划分,再用垂直 $x$ 轴的直线进行划分……

那么,假设现在要用垂直 $x$ 轴的直线划分在 $a[0:n-1]$ 数组内某个整数区间 $[l,r)$ 上的这些点,

首先初始化该点的空间范围,假设 $mid = \left\lfloor {\frac{{l + r}}{2}} \right\rfloor$,作为 $[l,r)$ 按 $x$ 坐标从小到大排序时的中位点位置,

然后用 nth_element(st,st+n,ed) 线性时间复杂度地将 $[l,r)$ 分成了  $[l,mid)$ 和 $[mid+1,r)$ 两部分,而 $a[mid]$ 则存储了中位点,

然后递归两个区间(作为左右儿子),当然,这两个区间则要用垂直 $y$ 轴的直线进行划分,以此类推。

(其中,调用 nth_element(st,st+n,ed) 方法可以求得 $[st,ed)$ 区间内所有元素中第 $n$ 小的元素,并把它放在第 $n$ 个位置上。注意,下标是从 $st+0$ 开始计数的,也就是说求第 $0$ 小的元素就是最小的数,同样地,第 $0$ 个位置是最前面的位置。)

举例:

HDU 4347 - The Closest M Points - [KDTree模板题]

2.2、查询 $k$ 近邻

$k$ 近邻是指找到第 $k$ 近的点,需要维护一个大顶堆,维护当前 $k$ 个点中的最远距离,如果当前点比最远距离要小,则更新大根堆,而且利用最远距离可以减掉那些不在当前第 $k$ 距离内的区间。


AC代码:

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std; const int maxn=5e4+;
const int maxk=;
int k,idx;
struct Point
{
int x[maxk];
bool operator<(const Point &o)const
{
return x[idx]<o.x[idx];
}
void print()
{
for(int i=;i<k;i++) printf("%d%c",x[i],(i==k-)?'\n':' ');
}
}poi[maxn];
typedef pair<double,Point> P;
priority_queue<P> Q;
void clear(priority_queue<P> &Q)
{
if(Q.empty()) return;
priority_queue<P> tp;
swap(Q,tp);
}
struct kdTree
{
#define sqr(x) ((x)*(x))
#define ls (rt<<1)
#define rs (rt<<1|1)
Point o[maxn<<];
int son[maxn<<]; void build(int rt,int l,int r,int dep)
{
if(l>r) return;
son[rt]=r-l, son[ls]=son[rs]=-;
idx=dep%k;
int mid=(l+r)>>;
nth_element(poi+l,poi+mid,poi+r+);
o[rt]=poi[mid];
build(ls,l,mid-,dep+);
build(rs,mid+,r,dep+);
}
void query(int rt,Point p,int m,int dep)
{
if(son[rt]==-) return;
P nd(,o[rt]);
for(int i=;i<k;i++) nd.fi+=sqr(nd.se.x[i]-p.x[i]);
int dim=dep%k, x=ls, y=rs; bool fg=;
if(p.x[dim]>=o[rt].x[dim]) swap(x,y);
if(~son[x]) query(x,p,m,dep+);
if(Q.size()<m) Q.push(nd), fg=;
else
{
if(nd.fi<Q.top().fi) Q.pop(), Q.push(nd);
if(sqr(p.x[dim]-o[rt].x[dim])<Q.top().fi) fg=;
}
if(~son[y] && fg) query(y,p,m,dep+);
}
}kdt; int n;
int main()
{
while(cin>>n>>k)
{
for(int i=;i<n;i++) for(int j=;j<k;j++) scanf("%d",&(poi[i].x[j]));
kdt.build(,,n-,); int t; cin>>t;
stack<Point> ans;
while(t--)
{
Point qry;
for(int i=;i<k;i++) scanf("%d",&qry.x[i]);
int m; scanf("%d",&m); clear(Q);
kdt.query(,qry,m,); printf("the closest %d points are:\n",m);
while(Q.size()) ans.push(Q.top().se), Q.pop();
while(ans.size()) ans.top().print(), ans.pop();
}
}
}