hdu 6097 Mindis (反演点)

13 篇文章 0 订阅
3 篇文章 0 订阅

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6097

题目大意:给你圆内两个点P、Q,求圆上一点D,使得 |QD|+|PD|最短

在此之前我们先了解一下反演点的知识,参照百度百科:

一般指二维反演中的点。
二维上反演以一个特定的反演圆为基础:圆心O为反演中心,圆半径为常数k,把点P反演为点P'就是使得OP×OP'=r^2.
如点P在圆外可这样作:过点P作圆的切线(两条),两个切点相连与OP连线交点就是点P'.
如点P在圆内就把这一过程反过来即可:连结OP,过点P作直线垂直于OP,直线与圆的交点处的切线的交点就是点P'.
如点P在圆上,反演后仍是它自身.按上述方法都可用尺规作图完成.

题目思路:

那么可以知道因为OP*OP'=r*r

那么易证明对于圆上任意一点T,▲OP'T与▲OTP相似 (因为两对边比例相等且夹角相等都为∠POT)

知道这个结论之后,我们求的 min|QD|+|PD| 可以转化为求 min|Q'D|+|P'D| 然后再按边的比例即可得到解

如果直线P'Q'与圆没有交点,那么答案为PQ中垂线与圆的交点。

否则答案为|P'Q'|

#include<bits/stdc++.h>
using namespace std;
const double eps=1e-8;
struct point
{
	double x,y;
};
double distance1(point p1,point p2)//求两点的距离 
{
	return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
point intersection(point u1,point u2,point v1,point v2)//求两条直线交点 
{
	point ret=u1;
	double t=((u1.x-v1.x)*(v1.y-v2.y)-(u1.y-v1.y)*(v1.x-v2.x))/((u1.x-u2.x)*(v1.y-v2.y)-(u1.y-u2.y)*(v1.x-v2.x));
	ret.x+=(u2.x-u1.x)*t;
	ret.y+=(u2.y-u1.y)*t;
	return ret;
}
void intersection_line_circle(point c,double r,point l1,point l2,point& p1,point& p2)//求直线与圆交点 
{
	point p=c;
	double t;
	p.x+=l1.y-l2.y;
	p.y+=l2.x-l1.x;
	p=intersection(p,c,l1,l2);
	double temp=r*r-distance1(p,c)*distance1(p,c);
	t=sqrt(temp)/distance1(l1,l2);
	p1.x=p.x+(l2.x-l1.x)*t;
	p1.y=p.y+(l2.y-l1.y)*t;
	p2.x=p.x-(l2.x-l1.x)*t;
	p2.y=p.y-(l2.y-l1.y)*t;
}
double ptoline(point p,point l1,point l2) 
{ 
	point midp; 
	midp.x=(l1.x+l2.x)/2.0;
	midp.y=(l1.y+l2.y)/2.0;
	return distance1(p,midp);
}
point invpoint(point p,double k)//求点p对于原点O的反演点 
{
	//反演点为向量x伸长 k倍
	point invp;
	invp.x=p.x*k;
	invp.y=p.y*k;
	return invp;
}
double solve()
{
	double r,ans;
	point O,P,Q,invP,invQ,PQmid;
	scanf("%lf",&r);
	scanf("%lf%lf%lf%lf",&P.x,&P.y,&Q.x,&Q.y);
	O.x=O.y=0;//原点O为圆心 
	PQmid.x=(P.x+Q.x)/2.0;
	PQmid.y=(P.y+Q.y)/2.0;
	double k=(r*r/distance1(P,O)/distance1(P,O));
	invP=invpoint(P,k);
	invQ=invpoint(Q,k);
	if(distance1(P,O)<eps) ans=2.0*r;
	else if(r<ptoline(O,invP,invQ)) //两个反演点的连线在圆外,则答案为垂直平分线与圆的交点
	{
		point p1,p2;
		intersection_line_circle(O,r,O,PQmid,p1,p2);//求直线与圆交点 
		ans=min(distance1(P,p1)+distance1(Q,p1),distance1(P,p2)+distance1(Q,p2));
	} 
	else 
	{
		//cout<<"反演点"<<endl;
		ans=distance1(invP,invQ);
		ans*=distance1(P,O)/r;
	}
	return ans;
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--) printf("%.8lf\n",solve());
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值