Time Limit: 1 second
Memory Limit: 32 MB
【问题描述】
聚类方法要求将空间中的点集,按照一点的方式进行归类,要求每一类中的点集相互之间的距离足够的“近”。聚类的一般方法是选取某一个点P,并用一个距离r作为度量,只要空间中的点Q距离点P的距离不超过r时,我们说点Q和点P是属于同一类的。
现在我们考虑这么一个问题:
给定二维空间中的N个点,并给定一个点P,你的任务就是求出在给定这N个点的点集中,距离点P的距离不超过r的最近点的距离。点A(x1,y1)和点B(x2,y2)之间的距离定义为dis(A,B)=|x1-x2|+|y1-y2|。
【输入格式】
输入数据的第一行为两个整数N (10≤N≤100,000)和r(1≤r≤100),分别表示点集中一共有N个不同的点,最近距离必须不超过r。
之后有N行,每行有两个整数X和Y(-10,000≤X,Y≤10,000),表示二维空间的一个点(X,Y)。
之后一行有一个整数Q(1≤Q≤10,000),表示询问的次数,之后Q行,每行有两个整数Px和Py,表示询问在给定的N个点中,距离点(Px,Py)的距离不超过r的最近点的距离。
距离点(Px,Py)的距离不超过r的点数不会超过100个。
【输出格式】
对于每个询问,输出最近距离,如果不存在最近点距离,输出“-1”。
【输入样例1】
5 1 1 3 2 6 5 3 -1 5 6 5 2 2 3 3 2
【输出样例1】
1 -1
【题解】
KD-tree的讲解。请在目录里面选kdtree分类。就可以看到一遍详解的文章。
这道题就是要求最邻近的点距离。
如果直接用kd-tree的模板,没法过。最后一个点会超时。
我们在看到当前点离分界面的距离比当前更新到的最短距离小的时候。会往另外一个儿子方向走。
那么我们可以加一句if 离分界面的距离 > r 则不往另一个儿子走。
因为大于r了。往另一个方向走肯定不能得出小于r的结果。没必要更新。
就是变成曼哈顿距离了。
nth_element(a + begin, a + m, a + 1 + end, cmp);
的范围是左闭右开。
所以要加1.
然后m左边就全是小于|等于a[m],右边全部大于||等于m。
【代码】
#include <cstdio>
#include <algorithm>
#include <cmath>
const int MAXN = 101000;
const int INF = 2100000000;
using namespace std;
struct data2
{
int wei[2];
};
int n, r, fenlie[MAXN] = { 0 },v,q,ans;
data2 a[MAXN],op;
double fc[2];
int cmp(data2 a, data2 b) //写成结构体比较好写比较函数。
{
if (a.wei[fenlie[v]] < b.wei[fenlie[v]])
return 1;
return 0;
}
void build(int begin, int end)
{
if (begin >= end)
return;
int m = (begin + end) >> 1;
for (int i = 0; i <= 1; i++)
{
double x = 0;
for (int j = begin; j <= end; j++)
x += a[j].wei[i];
x /= (end - begin + 1);
fc[i] = 0;
for (int j = begin; j <= end; j++)
fc[i] += (a[j].wei[i] - x)*(a[j].wei[i] - x);
fc[i] /= (end - begin + 1);
}
if (fc[0] > fc[1])
fenlie[m] = 0;
else
fenlie[m] = 1;
v = m;
nth_element(a + begin, a + m, a + 1 + end, cmp);
build(begin, m - 1);
build(m + 1, end);
}
void input_data()
{
scanf("%d%d", &n, &r);
for (int i = 1; i <= n; i++)
scanf("%d%d", &a[i].wei[0], &a[i].wei[1]);
build(1, n);
}
void query(int begin, int end)
{
if (begin > end)
return;
int m = (begin + end) >> 1;
int dis = abs(a[m].wei[0] - op.wei[0]) + abs(a[m].wei[1] - op.wei[1]);
if (dis < ans)
ans = dis;
if (begin == end)
return;
dis = abs(a[m].wei[fenlie[m]] - op.wei[fenlie[m]]);
if (op.wei[fenlie[m]] < a[m].wei[fenlie[m]])
{
query(begin, m - 1);
if (ans > dis && dis < r) //加上一句小于r的判断。不满足就不进入那个儿子。
query(m + 1, end);
}
else
{
query(m + 1, end);
if (ans > dis && dis < r)
query(begin, m - 1);
}
}
void output_ans()
{
scanf("%d", &q);
while (q--)
{
ans = INF;
scanf("%d%d", &op.wei[0], &op.wei[1]);
query(1, n);
if (ans <= r)
printf("%d
", ans);
else
printf("-1
");
}
}
int main()
{
//freopen("F:\rush.txt", "r", stdin);
input_data();
output_ans();
return 0;
}