两向量间夹角求法公式:
//计算x,y两点与o点形成夹角的弧度值
long double caltheta(int x, int y) {
int mult=0;
long double theta;
//求向量ox点乘向量oy的值(整型),输入时记录的p[]为相对于黑洞中心位置的坐标值
for(int i=0;i<n;i++){
mult+=p[x].at(i)*p[y].at(i);
}
//公式:向量a*向量b=a的长度*b的长度*cos<a,b>
if(mult*mult == P2O[x] * P2O[y]){//判断cos值是否为1或-1,避免sqrt精度问题导致的nan值结果
if (mult>0){ //cos值为1,theta=0;
return 0;
}else{ //cos值为-1,theta=pi ;
return 3.1415926535897932;
}
}else{ //其余情况根据公式求theta值
theta=acos(mult / sqrt(P2O[x] * P2O[y]));
return theta;
}
}
//两点间带曲线路程计算
long double calcirdis(int x, int y) {
long double a = P2O[x], b = P2O[y], c = P2P[x][y],ans;
/**分为三部分,1、黑洞表面曲线部分,2、x点到切点的距离,3、y点到切点的距离
x点到切点的距离:sqrt(a - r * r)
y点到切点的距离:sqrt(b - r * r)
黑洞表面曲线部分:(thete0-thete1-thete2)*半径
先调用caltheta(x, y)求角<XOY>的弧度thete0
再用 acos(sqrt(r*r / a))求出<xO切点> 的弧度 thete1
同理 acos(sqrt(r*r / b))求出<yO切点> 的弧度 thete2
**/
ans=caltheta(x, y)*r - acos(sqrt(r*r / a))*r - acos(sqrt(r*r / b))* r + sqrt(a - r * r) + sqrt(b - r * r);
return ans;
}
全部代码
#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
const int maxm=2000;
long long r,n,m;
vector<long long> O; //黑洞中心坐标(n维用数组放)
vector<long long> p[maxm]; //旅行点坐标
long double dis[2000][2000]; //两点间的旅行距离
long long P2P[maxm][maxm]; //两点间直线距离的平方值
long long P2O[maxm]; //点到黑洞中心距离的平方值
//计算x,y两点与o点形成夹角的弧度值
long double caltheta(int x, int y) {
int mult=0;
long double theta;
//求向量ox点乘向量oy的值(整型),输入时记录的p[]为相对于黑洞中心位置的坐标值
for(int i=0;i<n;i++){
mult+=p[x].at(i)*p[y].at(i);
}
//公式:向量a*向量b=a的长度*b的长度*cos<a,b>
if(mult*mult == P2O[x] * P2O[y]){//判断cos值是否为1或-1,避免sqrt精度问题导致的nan值结果
if (mult>0){ //cos值为1,theta=0;
return 0;
}else{ //cos值为-1,theta=pi ;
return 3.1415926535897932;
}
}else{ //其余情况根据公式求theta值
theta=acos(mult / sqrt(P2O[x] * P2O[y]));
return theta;
}
}
//两点间带曲线路程计算
long double calcirdis(int x, int y) {
long double a = P2O[x], b = P2O[y], c = P2P[x][y],ans;
/**分为三部分,1、黑洞表面曲线部分,2、x点到切点的距离,3、y点到切点的距离
x点到切点的距离:sqrt(a - r * r)
y点到切点的距离:sqrt(b - r * r)
黑洞表面曲线部分:(thete0-thete1-thete2)*半径
先调用caltheta(x, y)求角<XOY>的弧度thete0
再用 acos(sqrt(r*r / a))求出<xO切点> 的弧度 thete1
同理 acos(sqrt(r*r / b))求出<yO切点> 的弧度 thete2
**/
ans=caltheta(x, y)*r - acos(sqrt(r*r / a))*r - acos(sqrt(r*r / b))* r + sqrt(a - r * r) + sqrt(b - r * r);
return ans;
}
//计算两点间直线距离的平方值(整型)
long long calP2P(int x, int y) {
long long ans=0;
for (int i = 0;i < n;i++) {
ans += (p[x].at(i) - p[y].at(i))*(p[x].at(i) - p[y].at(i));
//每个维度相减的平方,在求和
}
return ans;
}
//判断x点与y点的连线是否在黑洞外
bool isOut(int x,int y) {
long double a,b,c,d,h;
long long pm1=0,pm2=0;
//根据海伦-秦九韶公式计算黑洞中心O点到xy直线的距离h
a = sqrt(P2O[x]);
b = sqrt(P2O[y]);
c = sqrt(P2P[x][y]); //底
d = (a + b + c) / 2; //半周长
h = 2 * sqrt(d * (d - a) * (d - b) * (d - c)) / c; //高
//pm1为ox点乘yx;pm2为oy点乘yx;
for(int i=0;i<n;i++){
pm1+=p[x].at(i)*(p[x].at(i)-p[y].at(i));
pm2+=p[y].at(i)*(p[y].at(i)-p[x].at(i));
}
//若h大于r或者pm1或pm2小于等于零,则连线在黑洞外;
return (h >= r+0.000001||pm1<=0||pm2<=0);
}
//计算x点与y点之间的旅行距离,并将该距离赋值给dis[X][Y]和dis[Y][X]
void caldis(int x, int y) {
P2P[x][y] = calP2P(x, y); //先计算两点间直线距离的平方值
P2P[y][x] = P2P[x][y];
if (isOut(x,y)) { //判定两点间连线是否在黑洞之外
dis[x][y]=sqrt(P2P[x][y]); //若在黑洞之外则路程为P2P开平方
dis[y][x]=dis[x][y];
}else {
dis[x][y] = calcirdis(x, y); //若在黑洞内则调用曲线路程函数计算
dis[y][x] = dis[x][y];
}
}
int main(){
int in;
long double ans;
//n维,m个点,黑洞半径为r;
cin >> n >> m >> r;
//黑洞中心的坐标值
for (int i = 0;i < n;i++) {
cin >> in;
O.push_back(in);
}
//输入m个n维的点
for (int i = 0;i < m;i++) {
P2O[i] = 0;
for (int j = 0;j < n;j++) {
cin >> in;
p[i].push_back(in - O.at(j)); //记录坐标值为相对于黑洞中心位置的坐标值
P2O[i] += (in - O.at(j))*(in - O.at(j)); //记录该点到黑洞中心距离的平方值(整型)
}
}
//调用caldis函数对dis数组赋值,计算每个旅行点两两之间的距离,共m!次
for (int i = 0;i < m;i++) {
for (int j = i + 1;j < m;j++) {
caldis(i, j);
}
}
//输出结果
for (int i = 0;i < m;i++) {
ans = 0;
for (int j = 0;j < m;j++) {
ans = ans + dis[i][j];
}
printf("%.14Lf\n",ans);
}
return 0;
}