圆的反演 hdu 4773

欢迎关注更多精彩
关注我,学习常用算法与数据结构,一题多解,降维打击。

题目大意

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

给定2个不相交的圆以及圆外1点P。求过P并且与另两个圆相切(外切)的圆,这种圆有可能有多个。

基本思路

圆的反演有如下性质:

  1. 圆C的圆心为O,则如果有一个圆过点O,则该圆对C的反演是一条直线。反之直线可以反演成圆。
  2. 如果两个圆相切,则反演后的几何形状还是相切。

题目要求的是过点P的圆,可以把圆先以P为圆心(半径取1即可)进行反演,然后求公切线,再将切线反演成圆,判断是否外切。

内公切线反演后必有1个圆是内切

在这里插入图片描述
反演可以理解为一种凸镜反射,反射的特点就是位置会相反,远近会颠倒。
从上图可以看出,当圆处于公切线与P的另一边时,最终结果是内切。
两个圆的内部公切线必有1个圆是处于这种情况。所以只要求外公切线即可。

求解外公切线

在这里插入图片描述

根据圆的公切线性质可知,
∠ P 1 A P 1 ′ = ∠ P 2 B P 2 ′ ,设置值为 α \angle P1AP1'=\angle P2BP2',设置值为\alpha P1AP1=P2BP2,设置值为α

∴ △ O A P 1 ′ ∼ △ O B P 2 ′ , 相似比是 r 1 r 2 \therefore \triangle OAP1' \sim \triangle OBP2', 相似比是\frac {r_1}{r_2} OAP1OBP2,相似比是r2r1

P1’, P2’ 可以用P1, P2经过一个旋转得到。

P 1 ′ = [ c o s α − s i n α s i n α c o s α ] ⋅ ( P 1 − A ) + A P1' = \begin {bmatrix} cos\alpha&-sin\alpha\\sin\alpha&cos\alpha \end {bmatrix}\cdot (P1-A)+A P1=[cosαsinαsinαcosα](P1A)+A

P 2 ′ = [ c o s α − s i n α s i n α c o s α ] ⋅ ( P 2 − B ) + B P2' = \begin {bmatrix} cos\alpha&-sin\alpha\\sin\alpha&cos\alpha \end {bmatrix}\cdot (P2-B)+B P2=[cosαsinαsinαcosα](P2B)+B

另一边的公切线,相当于是逆时针旋转

P 1 ′ = [ c o s α s i n α − s i n α c o s α ] ⋅ ( P 1 − A ) + A P1' = \begin {bmatrix} cos\alpha&sin\alpha\\-sin\alpha&cos\alpha \end {bmatrix}\cdot (P1-A)+A P1=[cosαsinαsinαcosα](P1A)+A

P 2 ′ = [ c o s α s i n α − s i n α c o s α ] ⋅ ( P 2 − B ) + B P2' = \begin {bmatrix} cos\alpha&sin\alpha\\-sin\alpha&cos\alpha \end {bmatrix}\cdot (P2-B)+B P2=[cosαsinαsinαcosα](P2B)+B

通过相似三角形可以解出OA

c o s α = r 1 O A , s i n α = 1 − s i n 2 α cos\alpha =\frac {r_1}{OA}, sin\alpha = \sqrt{1-sin^2\alpha} cosα=OAr1,sinα=1sin2α

代码实现

#include<stdio.h>
#include<cmath>
#include <algorithm>
#include <vector>using namespace std;class Point {
public:double x, y;Point() {}Point(double a, double b) :x(a), y(b) {}Point(const Point &p) :x(p.x), y(p.y) {}void in() {scanf(" %lf %lf", &x, &y);}void out() {printf("%f %f\n", x, y);}double dis() {return sqrt(x * x + y * y);}Point operator -(const Point& p) const {return Point(x-p.x, y-p.y);}Point operator +(const Point& p) const {return Point(x + p.x, y + p.y);}Point operator *(double d)const {return Point(x *d, y *d);}Point operator /(double d)const {return Point(x / d, y / d);}void operator -=(Point& p) {x -= p.x;y -= p.y;}void operator +=(Point& p) {x += p.x;y += p.y;}void operator *=(double d) {x *= d;y *= d;}void operator /=(double d) {this ->operator*= (1 / d);}
};class Line {
public:Point front, tail;Line() {}Line(Point a, Point b) :front(a), tail(b) {}
};// https://oi-wiki.org//geometry/inverse/#%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99%E4%B8%8E%E6%8B%93%E5%B1%95%E9%98%85%E8%AF%BB
// 根据官方定义实现圆的反演
class Circle
{
public:Point center;double r;Circle(const Point &c, double a):center(c), r(a){}Circle() {}void in() {center.in();scanf("%lf", &r);}void out() {center.out();printf("%f\n", r);}// 不过圆心的圆进行反演,得到1个圆Circle invert(const Circle& A) {Circle B;double oa = (center - A.center).dis();B.r = r * r / 2 * (1.0 / (oa - A.r) - 1.0 / (oa + A.r));double ob = r * r / (oa + A.r) + B.r;B.center = center +  (A.center - center)* ob / oa;return B;}// 过圆心的圆进行反演,得到1条直线Point invert2line(const Circle& c) {return Point();}// 直线反演,得到圆Circle invert2circle(const Line& l) {Point dir = l.front - l.tail;dir /= dir.dis();Circle c(Point(0, 0), 0);// 计算投影Point cdir = center - l.tail;Point project =l.tail + dir*(dir.x * cdir.x + dir.y*cdir.y);// 点乘得到投影长度// 计算圆到直线的距离Point op = project - center;if (op.dis() < 1e-6)return c;// 直线与圆心重合非法// 求解圆上的最远点double d = r * r / op.dis();Point pf = center + op / op.dis() * d;c.center = (center + pf) / 2;c.r = d / 2;return c;}};// 根据相似三角形求解圆的同侧公切线
vector<Line> getTangentLine(const Circle &c1, const Circle &c2) {if (c1.r > c2.r)return getTangentLine(c2, c1);double costheta = 0, sintheta = 1;	// 两圆半径不同,公切线与圆心连线有交点,用相似三角形求解if (c2.r - c1.r > 1e-6) {double oa = (c1.center - c2.center).dis() / (c2.r / c1.r - 1);costheta = c1.r / oa;sintheta = sqrt(1 - costheta * costheta);}// 两个圆上与圆心边线的交点Point pa, pb;Point ab = (c1.center - c2.center);ab /= ab.dis();// 通过两边旋转求解两条公切线vector<Line> res;for (int i = 0; i < 2; i++, sintheta *= -1) {pa = ab;Point pap, pbp;pap.x = costheta * pa.x - sintheta * pa.y;pap.y = sintheta * pa.x + costheta * pa.y;pap = pap * c1.r;pap = pap + c1.center;pb = ab;pbp.x = costheta * pb.x - sintheta * pb.y;pbp.y = sintheta * pb.x + costheta * pb.y;pbp = pbp * c2.r;pbp = pbp + c2.center;res.push_back({pap, pbp});}return res;
}void solve() {Point P, Q, P1, Q1, M;int T;Circle c1, c2,c1invert, c2invert, OC;OC.r = 1;int x1, y1, r1, x2, y2, r2, x3, y3;scanf("%d", &T);while (T--) {scanf("%d %d %d %d %d %d %d %d", &x1, &y1, &r1, &x2, &y2, &r2, &x3, &y3);c1 = Circle(Point(x1, y1),r1);c2 = Circle(Point(x2, y2),r2);OC.center = Point(x3, y3);c1invert = OC.invert(c1);c2invert = OC.invert(c2);auto lines = getTangentLine(c1invert, c2invert);// 对直线进行反演回来,并判断是否与另两圆外切vector<Circle> circles;for (auto &l : lines) {Circle c = OC.invert2circle(l);if (c.r < 1e-6)continue; // 无法得到圆// 判断是否外切//printf("%.6f %.6f\n", (c1.center - c.center).dis(), c1.r+c.r);//printf("%.6f %.6f\n", (c2.center - c.center).dis(), c2.r+c.r);if (abs((c1.center - c.center).dis() - c1.r - c.r)>1e-6)continue;if (abs((c2.center - c.center).dis() - c2.r - c.r)>1e-6)continue;circles.push_back(c);}printf("%d\n", circles.size());for (auto &c : circles) {printf("%.6f %.6f %.6f\n", c.center.x, c.center.y, c.r);}}
}int main() {solve();return 0;
}/*
1
12 10 1 8 10 1 10 101 1 1 10 10 2 3 3
1 1 1 10 10 2 3 10
1 1 1 10 10 2 10 3
12 10 1 8 10 1 10 11
1 1 1 10 10 2 16 100
*/

本人码农,希望通过自己的分享,让大家更容易学懂计算机知识。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/107267.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决2K/4K高分屏下Vmware等虚拟机下Kail Linux界面显示问题

问题现象 在我们日常使用VirtualBox、Vmware workstation、Hyper-V等虚拟机安装使用Kali系统&#xff0c;在2K/4K高分辨率电脑下Kali系统界面显示太小&#xff0c;包括各种软件及命令终端字体均无法很直观的看出&#xff0c;影响我们的正常测试及使用。 常规处理思路 很多人…

软件设计师_计算机组成与体系结构

计算机组成与体系结构 文章目录 1.1 数据的表示1.1.1 进制的转换1.1.2 原码 反码 补码 移码1.1.3 浮点数运算 1.2 计算机结构1.3 Flynn分类法1.4 CISC和RISC1.5 流水线技术1.6 存储系统1.7 总线系统1.8 可靠性1.9 校验码 1.1 数据的表示 1.1.1 进制的转换 R进制转十进制 --&g…

【深度学习】Mini-Batch梯度下降法

Mini-Batch梯度下降法 在开始Mini-Batch算法开始之前&#xff0c;请确保你已经掌握梯度下降的最优化算法。 在训练神经网络时&#xff0c;使用向量化是加速训练速度的一个重要手段&#xff0c;它可以避免使用显式的for循环&#xff0c;并且调用经过大量优化的矩阵计算函数库。…

Pytest系列-内置标签skip和skipif 跳过测试用例的详细使用(5)

简介 skip和skipif&#xff0c;见名知意就是跳过测试&#xff0c;主要用于不想执行的代码&#xff0c;标记后&#xff0c;标记的代码不执行。希望满足某些条件才执行某些测试用例&#xff0c;否则pytest会跳过运行该测试用例实际常见场景&#xff1a;根据平台不同执行测试、跳…

VirtualBox宿主机和虚拟机文件互传设置

一、如图1、2、3步骤&#xff0c;设置共享粘贴板和拖放为双向 二、 在启动的虚拟机设置的里面&#xff0c;安装增强插件&#xff0c;然后重启虚拟机。 三、在网络位置就可以看到了

Linux调试器gdb

目录 一、关于Linux调试器gdb 二、gdb的操作 1、quit 2、l 3、r 4、b行号 info b 5、d断点编号 6、n 7、p[变量名] 8、s 9、bt 10、finish 11、display[变量名] 12、until行号 13、c 14、disable/enable断点编号 一、关于Linux调试器gdb 首先&#xff0c;我们…

简简单单教你如何用C语言实现获取当前所有可用网口!

一、获取本机所有可用网卡名 原理&#xff1a; 在 Linux 系统中&#xff0c;/proc 目录是一个位于内存中的伪文件系统。 /proc目录是内核提供给我们的查询中心&#xff0c;通过查询该目录下的文件内容&#xff0c;可以获取到有关系统硬件及当前运行进程的信息&#xff0c;如…

全栈工程师必须要掌握的前端CSS技能

作为一名全栈工程师&#xff0c;在日常的工作中&#xff0c;可能更侧重于后端开发&#xff0c;如&#xff1a;C#&#xff0c;Java&#xff0c;SQL &#xff0c;Python等&#xff0c;对前端的知识则不太精通。在一些比较完善的公司或者项目中&#xff0c;一般会搭配前端工程师&a…

海外商城小程序如何开发

随着全球化的发展和人们对跨境购物的需求逐渐增加&#xff0c;海外商城小程序成为了众多电商平台的重要组成部分。本文将深入探讨如何搭建海外商城小程序&#xff0c;从技术实现到用户体验设计&#xff0c;为开发者提供专业且有深度的思考&#xff0c;以帮助他们打造出色的跨境…

U3D外包开发框架及特点

U3D&#xff08;Unity3D&#xff09;是一款流行的跨平台游戏开发引擎&#xff0c;用于创建2D和3D游戏以及交互性应用程序。U3D有许多常用的开发框架和库&#xff0c;这些框架和库可以扩展其功能&#xff0c;使开发人员更轻松地构建游戏和应用程序。以下是一些常用的U3D开发框架…

【校招VIP】测试技术考点之单元测试集成测试

考点介绍&#xff1a; 单元测试,集成测试的区别是&#xff1a;方式不同、粒度不同、内容不同。单元测试用用于验证编码单元的正确性。集成测试用于验证详细设计。体现了测试由小到大、又内至外、循序渐进的测试过程和分而治之的思想。 测试技术考点之单元测试&集成测试-相…

C#进阶 多个泛型约束

using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine;public class A02_Generic : MonoBehaviour {[ContextMenu("测试Start")]// Start is called before the first frame updatevoid Start(){Person…