hdu6097 Mindis

本文探讨了一种解决几何问题的方法,通过圆的反演将原问题转化为寻找两点间最短路径的问题。文章详细介绍了如何利用点到圆的反演性质,将原问题转换,并给出了具体的算法实现,包括使用C++代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://acm.hdu.edu.cn/showproblem.php?pid=6097

我发现仅仅看着那些圆的反演的性质,然而还是不会做题。。。。

我们把p点对圆c进行反演得到p2,对q进行反演得到q2,

由于三角形ODP~三角形OP2D,因为|OD|*|OD|=r^2=|OP|*|OP2|,所以相似,那么|PD|/|DP2|=|OD|/|OP2|

这样P2D+Q2D最小就等价于PD+QD最小

那么P2Q2如果与圆有交点,那么两点之间线段最短,D就是与圆的交点

否则,则是道P2Q2的中垂线与圆的交点最短,直接算出中点mid与C的圆心(0,0)的角度,然后把D算出来就行了。

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

const double eps=1e-12;

inline int sgn(double x)
{
	if(x>-eps && x<eps) return 0;
	if(x>0) return 1;
	else 	return -1;
}

struct point
{
	double x,y;
	point(double a=0,double b=0)
	{
		x=a;y=b;
	}
	point operator + (const point b)const
	{
		return point(x+b.x,y+b.y);
	}
	point operator - (const point b)const
	{
		return point(x-b.x,y-b.y);
	}
	point operator * (const double t)const
	{
		return point(t*x,t*y);
	}
};
struct circle
{
	point o;
	double r;
};
inline double det(const point a,const point b)
{
	return a.x*b.y-a.y*b.x;
}
inline double sqr(double x) {return x*x;}
inline double dist(const point a,const point b)
{
	return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));
}
inline double mysqrt(double x)
{
	x=max(0.0,x);
	return sqrt(x);
}

circle c;
point p,q,d;
double ans;

inline void prework()
{
	c.o=point(0,0);
	scanf("%lf",&c.r);
	scanf("%lf%lf%lf%lf",&p.x,&p.y,&q.x,&q.y);
}

inline point inverse_point(point p,circle a)
{
	point res;
	double len=dist(p,a.o);
	double len2=a.r*a.r/len;
	res=a.o+(p-a.o)*(len2/len);
	return res;
}

inline void circle_cross_line(point a,point b,point o,double r,point ret[],int &num)
{
	double x0=o.x,y0=o.y;
	double x1=a.x,y1=a.y;
	double x2=b.x,y2=b.y;
	double dx=x2-x1,dy=y2-y1;
	double A=dx*dx+dy*dy;
	double B=2*dx*(x1-x0)+2*dy*(y1-y0);
	double C=sqr(x1-x0)+sqr(y1-y0)-sqr(r);
	double delta=B*B-4*A*C;
	num=0;
 	if(sgn(delta)>=0)
    {
		double t1=(-B-mysqrt(delta))/(2*A);
		double t2=(-B+mysqrt(delta))/(2*A);
     	if(sgn(t1-1)<=0&&sgn(t1)>=0)
        	ret[num++]=point(x1+t1*dx,y1+t1*dy);
    	if(sgn(t2-1)<=0&&sgn(t2)>=0)
        	ret[num++]=point(x1+t2*dx,y1+t2*dy);
    }
}

inline void mainwork()
{
	if(sgn(p.x-q.x)==0 && sgn(p.y-q.y)==0)
	{
		ans=(c.r-dist(c.o,p))*2;
		return;
	}
	point p2=inverse_point(p,c);
	point q2=inverse_point(q,c);
	double d=fabs(det(p2-c.o,q2-c.o))/dist(p2,q2);
	if(sgn(d-c.r)<=0)
	{
		point ret[3];int num=0;
		circle_cross_line(p2,q2,c.o,c.r,ret,num);
		point d=ret[0];
		ans=dist(p,d)+dist(q,d);
	}
	else
	{
		point mid=point((p.x+q.x)/2,(p.y+q.y)/2);
		double a=atan2(mid.y,mid.x);
		point d=point(c.r*cos(a),c.r*sin(a));
		ans=dist(p,d)+dist(q,d);
	}
}

inline void print()
{
	printf("%.8f\n",ans);
}

int main()
{
	int t;
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值