CCFCSP星际旅行(95)

两向量间夹角求法公式:
公式
在这里插入图片描述

 //计算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;
}

参考:CCFCSP认证202009-4星际旅行满分题解(C++)作者:qq_34653042

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值