牛客小白月赛24:A-最短路

题目描述

牛能在家里遇到了一个问题,他现在想要去找牛可乐并向他请教这个问题,现在他已经知道了牛可乐家的坐标,并且他很容易找到了通往牛可乐家里的最短路(一条直线)。
但是,平面上有一个圆形区域,这个圆形区域内是被诅咒过的地方,所以牛能不能进入这块圆形区域的内部,所以牛能现在找不到通往牛可乐家的最短路了,请帮助他解决这个问题!

保证牛能家和牛可乐家不在诅咒区域内。
输入描述:

第一行四个整数 x1, y1, x2, y2x1,y1,x2,y2,(x1, y1)(x1,y1) 表示牛能家的坐标,(x2, y2)(x2,y2) 表示牛可乐家的坐标。

第二行三个整数 x3, y3, rx3,y3,r,(x3, y3)(x3,y3) 表示被诅咒的圆形区域的位置的圆心坐标,rr 表示这个圆形区域的半径。

输出描述:

在一行中输出牛能从家到牛可乐家的最短路。

示例1
输入
-1 -1 1 1
0 0 1
输出
3.570796
示例2
输入
1 1 2 2
0 0 1
输出
1.414214
备注:

− 1 e 5 < = x 1 , y 1 , x 2 , y 2 , x 3 , y 3 < = 1 e 5 , 1 < = r < = 1 e 5 -1e^{5}<=x1,y1,x2,y2,x3,y3<=1e^{5},1<=r<=1e^{5} 1e5<=x1,y1,x2,y2,x3,y3<=1e5,1<=r<=1e5
如果标准答案是 a a a,你的答案是 b b b
当满足 f a b s ( a − b ) m a x ( 1.0 , f a b s ( a ) ≤ 1 0 − 6 \frac{fabs(a-b)}{max(1.0,fabs(a)}\leq10^{-6} max(1.0,fabs(a)fabs(ab)106你的答案被认为是正确的。

Solution

  • 首先判断两点连成的直线是否与圆相交,使用点积正负以及圆心到直线距离这两种方法判断,直线与圆不相交的情况下最短路为两点之间直线距离。
  • 当两点直线与圆相交时,最短距离为两点与圆切线的长度,加上圆上两个切点之间的弧长(后续画个图演示一下吧)
#include <bits/stdc++.h>
#define LINF 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
static const int N=5e2+5;
const double eps=1e-6;
const ll mod=1e9+7;
double R;
struct Point{
    double x,y;
}p[3];
double dis(Point p1,Point p2){		//两点距离
    return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
double Dot(Point p1,Point p2,Point p3){	//点积
    return (p2.x-p1.x)*(p2.x-p3.x)+(p2.y-p1.y)*(p2.y-p3.y);
}
bool check(Point p1,Point p2,Point p3){		//判断点积是否为负
    return Dot(p1,p2,p3)<=0||Dot(p2,p1,p3)<=0;  // >=90°
}
double judge(Point p1,Point p2,Point p3){	
    double a=dis(p1,p2),b=dis(p1,p3),c=dis(p2,p3);
    double p=(a+b+c)/2;
    double S=sqrt(p*(p-a)*(p-b)*(p-c));	//海伦公式求面积
    return (2*S/a)>=R;	//判断点到直线距离是否大于R
}
double ang(Point p1,Point p2,Point p3){
    double a=dis(p1,p2),b=dis(p1,p3),c=dis(p2,p3);	
    return acos((b*b+c*c-a*a)/(2*b*c));	//余弦公式求acos
}
int main(){ 
    for(int i=0;i<3;++i) scanf("%lf%lf",&p[i].x,&p[i].y);
    scanf("%lf",&R);
    if(check(p[0],p[1],p[2])||judge(p[0],p[1],p[2])){
        printf("%.6f\n",dis(p[0],p[1]));
        return 0;
    }
    double d13=dis(p[0],p[2]),d12=dis(p[0],p[1]),d23=dis(p[1],p[2]);
    double d1=sqrt(d13*d13-R*R),d2=sqrt(d23*d23-R*R);
    double d3=R*(ang(p[0],p[1],p[2])-acos(R/d13)-acos(R/d23));
    printf("%.6f\n",d1+d2+d3);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值