uva10180

 题目大意:

给你两个点,代表拔河的两只队伍的位置。有这样一个柱子,以原点为圆心,r为半径。那么拔河两只队伍拉的绳子很可能绕到柱子上,给你两个队伍的位置以及圆柱的半径,求拔河用绳子的最短长度。

 

算法设计:

如果拔河的连线和圆柱不想交,那么就是两支队伍之间的距离。

如果相交,那么就是3段距离之和,中间是一段圆弧。如何计算,首先由余弦定理计算出两支队伍两线与圆心所形成的圆心角,有了这个,我们又很容易求出最有两边两个直角三角形的角,然后将三段距离求和。

 

主要是如何判断线段是否和圆相交?

首先如果圆心到线段所形成的直线的距离小于半径,那么直线肯定和圆相交,但是线段不一定。但是如果(如下图)OA和OB在圆心到直线垂线的两侧,必定相交。图中是在同侧,所以可能不相交。

 

其他情况就是不相交了。

 

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
using namespace std;

struct POINT 
{
	double x,y;
};
struct LINE
{
	struct POINT p1;
	struct POINT p2;
};
struct LINE l1;


//叉积
double xmult(POINT p1, POINT p2, POINT p0) {
	return (p1.x - p0.x) * (p2.y - p0.y) -
		(p2.x - p0.x) * (p1.y - p0.y);
}

double GetDis(POINT p1, POINT p2)
{
	return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}
double GetLen(double r)
{
	double len,dis;
	double a, b, c;
	if (l1.p1.x == l1.p2.x)
	{
		b = 0;
		c = -l1.p1.x;
		a = 1;
	}
	else
	{
		b = -1;
		a = (l1.p1.y - l1.p2.y) / (l1.p1.x - l1.p2.x);
		c = l1.p1.y - a * l1.p1.x;
	}
	dis = fabs(c) / sqrt(a * a + b * b);
	POINT p,orig;
	orig.x = 0.0;
	orig.y = 0.0;
	p.x = l1.p2.y - l1.p1.y;
	p.y = l1.p1.x - l1.p2.x;
/*这句话就是判断相交的情况,注意后面那个叉积就是判断OA,OB在垂线的两侧。估计你会对我的p点有疑问,为何org和p的连续就是垂线啦?其实是这样的,我们知道org和p的连线肯定垂直于AB的连线,那么和AB向量垂直的向量不就是垂线吗?AB *(l1.p2.y - l1.p1.y, l1.p1.x - l1.p2.x)肯定=0.所以啊,原点和P的连线就是垂线。*/	if (dis  < r && xmult(p, l1.p1, orig) * xmult(p, l1.p2, orig) < 0.0)
	{
		
		double ab = sqrt(l1.p1.x * l1.p1.x + l1.p1.y * l1.p1.y);
		double ac = sqrt(l1.p2.x * l1.p2.x + l1.p2.y * l1.p2.y);
		double bc = GetDis(l1.p1, l1.p2);
		double angA = acos((ab * ab + ac * ac - bc * bc) /( 2.0 * ab * ac));
		double angB = acos(r / ab);
		double angC = acos(r / ac);
		double angl = angA - angB - angC;
		len = ab * sin(angB) + ac * sin(angC) + angl * r;
		return len;	
	}
	else
	{
		return GetDis(l1.p1, l1.p2);
	}

}
int main()
{
	int n;
	double R;
	scanf("%d", &n);
	while (n --)
	{
		scanf("%lf %lf %lf %lf %lf", &l1.p1.x, &l1.p1.y, &l1.p2.x, &l1.p2.y, &R);
		printf("%.3lf\n",GetLen(R));
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值