题意:
给出你一个球的球心坐标和球的半径,然后给出你起点和终点的坐标,问你想要从起点走到终点的最短路径是多长,路径不可以穿过球体,需要从表面绕过。
思路:
很明显分为两种情况
1.
1.
1.最短距离就是两点之间的连线。
2.
2.
2.最短距离需要经过圆的表面。
先考虑,最短距离就是两点连线的情况。设圆心 O O O到线段 s t st st的距离为 d d d,注意这里是到线段的距离,假如 d ≥ r d \geq r d≥r,那么两点就可以连线且不经过圆直接到达。
再考虑经过圆的表面,通过
s
,
t
s,t
s,t两点做球的切线,设两个切点为
O
1
,
O
2
O_1,O_2
O1,O2,那么最短距离就是
s
−
>
O
1
+
t
−
>
O
2
+
s->O_1 + t->O_2 +
s−>O1+t−>O2+弧
O
1
,
O
2
O_1,O_2
O1,O2的长度。
而弧的长度我们考虑可以通过对应的圆心角求出,而圆心角用余弦定理求出,这样就都解决。
点到线段距离套用的蓝书的板子。
#include <bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const db PI = acos(-1.0);
int dcmp(double x) {//判符号
if(fabs(x) <= eps) return 0;
if(x > 0) return 1;
else return -1;
}
struct Point
{
double x,y,z;
Point(){}
//定义运算
Point(double _x,double _y,double _z){x = _x;y = _y;z = _z;}
Point operator + (const Point &b)const{
return Point(x+b.x,y+b.y,z+b.z);
}
Point operator - (const Point &b)const{
return Point(x-b.x,y-b.y,z-b.z);
}
};
typedef Point Vector;
double pf(double x) { return x * x; }
double dis(Point a,Point b) {
return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) + (a.z-b.z)*(a.z-b.z));
}
double dot(Vector a,Vector b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
double Length(Vector a) {
return sqrt(dot(a,a));
}
Vector cross(Vector a,Vector b) {//注意点积是一个向量
return Vector(a.y * b.z - b.y * a.z,a.z * b.x - b.z * a.x,a.x * b.y - b.x * a.y);
}
bool equal(Point a,Point b) {
return (a.x == b.x && a.y == b.y && a.z == b.z);
}
double distosegment(Point p,Point a,Point b) {//点到线段的距离
if(equal(a,b)) return Length(p-a);
Vector v1 = b - a,v2 = p - a,v3 = p - b;
if(dcmp(dot(v1,v2)) < 0) return Length(v2);
else if(dcmp(dot(v1,v3)) > 0) return Length(v3);
else return Length(cross(v1,v2)) / Length(v1);
}
int main() {
double r,ans;
Point o,s,t;
int T;scanf("%d",&T);
while(T--) {
scanf("%lf%lf%lf%lf",&o.x,&o.y,&o.z,&r);
scanf("%lf%lf%lf%lf%lf%lf",&s.x,&s.y,&s.z,&t.x,&t.y,&t.z);
Vector st(t-s),so(o-s);
double dst = dis(s,t);
double dost = distosegment(o,s,t);
if(equal(s,t)) {
ans = 0;
printf("%.8f\n",ans);
continue;
}
if(fabs(dost - r) < eps || dost - r > eps) {//线段不经过球 注意等于的判断情况
ans = dst;
printf("%.8f\n",dst);
continue;
}
double dso = dis(o,s),dto = dis(o,t);
double t1 = acos((pf(dst) + pf(dso) - pf(dto)) / (2.0 * dso * dst));
double t2 = acos((pf(dto) + pf(dst) - pf(dso)) / (2.0 * dst * dto));
double angle1 = acos((pf(dso) + pf(dto) - pf(dst)) / (2.0 * dso * dto));
double angle2 = acos(r/dso),angle3 = acos(r/dto);
double alpha = angle1 - angle2 - angle3;
double dd = r * alpha;
ans = dso * sin(angle2) + dto * sin(angle3) + dd;
printf("%.8f\n",ans);
}
return 0;
}