题目描述:
现有一块大奶酪,它的高度为 ℎh,它的长度和宽度我们可以认为是无限大的,奶酪中间有许多 半径相同 的球形空洞。我们可以在这块奶酪中建立空间坐标系,在坐标系中,奶酪的下表面为 z=0,奶酪的上表面为 z=h。
现在,奶酪的下表面有一只小老鼠 Jerry,它知道奶酪中所有空洞的球心所在的坐标。如果两个空洞相切或是相交,则 Jerry 可以从其中一个空洞跑到另一个空洞,特别地,如果一个空洞与下表面相切或是相交,Jerry 则可以从奶酪下表面跑进空洞;如果一个空洞与上表面相切或是相交,Jerry 则可以从空洞跑到奶酪上表面。
位于奶酪下表面的 Jerry 想知道,在不破坏奶酪的情况下,最少经过多少个空洞才能够跑到奶酪的上表面去?
核心思想:看到最少/小,利用BFS
代码如下:
#include<stdio.h>
#include<math.h>
typedef struct circle {
long long int x;
long long int y;
long long int z;
int mark;//标记是否被访问过
}Circle;
typedef struct qu {
long long int x;
long long int y;
long long int z;
int d;//第几次
}Qu;
long long int dist(Qu now, Circle c)
{
return (now.x - c.x) * (now.x - c.x) + (now.y - c.y) * (now.y - c.y) + (now.z - c.z) * (now.z - c.z);
}
//找的球应该是让高度不断增加的
int main()
{
int T;
scanf("%d", &T);
for (int i = 0; i < T; i++) {
int n;
long long int h, r;
scanf("%d %lld %lld", &n, &h, &r);
Circle c[1010];
for (int j = 0; j < n; j++) {
scanf("%lld %lld %lld", &c[j].x, &c[j].y, &c[j].z);
c[j].mark = 0;
}
Qu s;//开始的球也可以有多个
int front, rear;
front = rear = -1;
Qu q[1010];
int flag1 = 1;
for (int j = 0; j < n; j++) {
if (c[j].z <= r) {
s.x = c[j].x;
s.y = c[j].y;
s.z = c[j].z;
s.d = 1;
rear++;
q[rear] = s;
c[j].mark = 1;
flag1 = 0;
}
}
//第一个球都找不到那么就直接输出-1
if (flag1) {
printf("-1\n");
continue;
}
int flag = 1;
while (front < rear) {
front++;
Qu now = q[front];
if (now.z + r >= h) {
printf("%d\n", now.d);
flag = 0;
break;
}
//判断相切与相交
for (int k = 0; k < n; k++) {
if (dist(now, c[k]) <= 4*r*r&&c[k].mark==0) {
rear++;
Qu next;
next.x = c[k].x;
next.y = c[k].y;
next.z = c[k].z;
next.d = now.d + 1;
q[rear] = next;
c[k].mark = 1;
}
}
}
//BFS结束还没有找到,说明没有
if(flag) printf("-1\n");
}
return 0;
}
需要注意的地方:
1.计算dist时,不要用开方,因为当输入数据过大时,sqrt传参要求时double,而输入数字又为整数,会有误差
2.因此用dist的平方<=4*r*r,但注意要用long long int类型的数据,不然数据过大会溢出
3.一开始犯的错误是找第一个球也可以有多个,当时默认只要找到一个球就可以了。
4.以前写题都是用vis多维数组记录是否被访问,但对于这个题这样做会有很大的空间浪费,一个球可以看成一个集合,因此用了代码中mark标记这种形式来判断是否访问过