hdu 6097 Mindis(圆上一点到圆内(距圆心相等的)两点的距离和最短)

Mindis

题目链接:Mindis

题意:圆内或者圆周上有两个点 p q,圆心为 o ,并且op=oq,让你在圆上找一点 d ,使得dp+dq最小

官方题解:
这里写图片描述

为什么是中垂线上的点取得最小值?
个人理解应该是类似于这种情况吧,不过任谁做这道题时首先想到的应该都是中垂线吧。。
这里写图片描述

还有就是极端情况,做出反演点(什么是反演点

如果直线 pq 与圆有交点,则答案为两点间距离
否则答案就在中垂线上取到

看着题解写代码很简单,问题就是这些结论怎么证的(蒻蒻记住结论,求路过的大佬解惑)。。。

代码:

#include<bits/stdc++.h>
using namespace std;

const double eps=1e-8;

struct point//点
{
    double x,y;
    point() {}
    point(double X,double Y)
    {
        x=X,y=Y;
    }
};

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 dis(point p1,point p2)//两点间距离
{
    return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}

double disptoline(point p,point l1,point l2)//点到直线的距离
{
    return fabs(xmult(p,l1,l2))/dis(l1,l2);
}

int intersect_line_circle(point c,double r,point l1,point l2)//判定直线是否与圆相交
{
    return disptoline(c,l1,l2)<r+eps;
}

int main()
{
    int T_T;
    double r,ans;
    point p,q,o(0,0);
    scanf("%d",&T_T);
    while(T_T--)
    {
        scanf("%lf%lf%lf%lf%lf",&r,&p.x,&p.y,&q.x,&q.y);
        double op=dis(p,o);
        if(p.x==q.x&&p.y==q.y)//特判两点在同一位置
            ans=2.0*(r-op);
        else
        {
            point p1(r*r/op/op*p.x,r*r/op/op*p.y),q1(r*r/op/op*q.x,r*r/op/op*q.y);//p'和q'
            if(intersect_line_circle(o,r,p1,q1))//判定直线是否与圆相交
                ans=dis(p1,q1)*op/r;
            else
            {
                point mid((q1.x+p1.x)/2,(q1.y+p1.y)/2);//两个反演点的中点
                double k=r/dis(mid,o);//比例
                point d(k*mid.x,k*mid.y);//得到中垂线上的点
                ans=2*dis(d,q);//两点到中垂线的距离相等所以×2
            }
        }
        printf("%.7lf\n",ans);
    }
    return 0;
}

ps:解释一下代码中的ans=dis(p1,q1)*op/r
反演点有一个定理:

                            dpdp=opop    (d为圆上任意一点)

opop=r2

dp=dpop2r2=dpopr
同理可得 dq=dqop2r2=dqoqr
op=oq ,因此ans=dis(p1,q1)*op/r

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页