圆的反演 hdu 4773

news2024/12/23 14:46:01

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

题目大意

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 10

1 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.coloradmin.cn/o/1006124.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用TortoiseGit导出两次提交时间之间的差异文件

同时选择两个提交时间&#xff0c;右键后点击Compare revisions 多选需要导出的待发布的文件&#xff0c;然后右键点击Export selection to... 在弹窗中选择文件夹&#xff08;导出待发布的文件&#xff09; 导出效果&#xff08;目录&#xff09; 导出效果&#xff08;文件&am…

MGR新节点RECOVERING状态的分析与解决:caching_sha2_password验证插件的影响

起因 在GreatSQL社区上有一位用户提出了“手工构建MGR碰到的次节点一直处于recovering状态”&#xff0c;经过排查后&#xff0c;发现了是因为新密码验证插件caching_sha2_password导致的从节点一直无法连接主节点&#xff0c;帖子地址&#xff1a;(https://greatsql.cn/threa…

基于Android系统图书管理系统

摘要 随着移动终端使用率的快速增加&#xff0c;Android智能产品已日益成为越来越多的人们选择的移动终端产品。伴随着Android智能手机与平板电脑已经在我们生活大量的使用&#xff0c;越来越多的基于Android开发平台的应用也随之产生。 便捷的图书检索和借阅&#xff1a;用户可…

Java“牵手”京东商品列表页数据采集+商品价格数据排序,商品销量排序数据,京东商品API采集方法

京东商品列表API是京东平台提供给开发者的应用程序编程接口&#xff0c;通过API可以获取京东平台上商品列表数据。 京东商品列表API可以提供多种不同的推荐商品列表API接口&#xff0c;开发者可以根据自己的需求选择适合自己的接口。其中&#xff0c;最常用的是基于用户反馈的…

项目经理如何做好跨部门的沟通与协作?

对项目经理来说沟通不良&#xff0c;会对项目造成严重影响&#xff0c;跨部门沟通更是项目管理中的难题。原本应该合作解决的问题&#xff0c;到了跨部门会议上&#xff0c;又各说各话&#xff0c;找不到共识。 在不同部门各有不同立场与利益的情况下&#xff0c;怎样才能把话…

Open Interpreter:OpenAI Code Interpreter的开源实现|本地化|可联网

如果你对这篇文章感兴趣&#xff0c;而且你想要了解更多关于AI领域的实战技巧&#xff0c;可以关注「技术狂潮AI」公众号。在这里&#xff0c;你可以看到最新最热的AIGC领域的干货文章和案例实战教程。 一、前言 今年7月&#xff0c;OpenAI发布了一个强大的插件&#xff0c;名…

什么是第三方软件测试?

测试机构 什么是软件测试&#xff1f; 软件测试是一个验证和验证应用程序功能以确定它是否满足要求的过程。这是在应用程序中发现缺陷并根据*终用户的要求检查应用程序功能的过程。 第三方软件检测机构是专门提供软件测试服务&#xff0c;其出具软件测试报告过程中可能运用到…

Object.keys和Object.values

Object.keys list:[],obj:{数据泄露: 5412, 数据传输: 3921, 数据篡改: 851392, 数据滥用: 59532 },//返回可枚举的属性数组console.log(Object.keys(this.obj)) // [数据泄露, 数据传输, 数据篡改, 数据滥用]Object.keys(this.obj).map(key>{this.list.push({title:key,val…

Java工作流系统,快速实现业务审批(源码)

前言 activiti工作流引擎项目&#xff0c;企业erp、oa、hr、crm等企事业办公系统轻松落地&#xff0c;请假审批demo从流程绘制到审批结束实例。 一、项目形式 springbootvueactiviti集成了activiti在线编辑器&#xff0c;流行的前后端分离部署开发模式&#xff0c;快速开发平…

700亿参数Llama 2训练加速195%!数据成为其提升效果的关键要素

Llama 2是Meta AI正式发布的最新一代开源大模型&#xff0c;达到了2万亿的token。精调Chat模型是在100万人类标注数据上训练。Llama 2在包括推理、编码、精通性和知识测试等许多外部基准测试中都优于其他开源语言模型。 Llama 2开启了全球范围内AI大型模型的共享新篇章。它包括…

第5章_freeRTOS入门与工程实践之模块使用说明与STM32CubeMX配置

本教程基于韦东山百问网出的 DShanMCU-F103开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id724601559592 配套资料获取&#xff1a;https://rtos.100ask.net/zh/freeRTOS/DShanMCU-F103 freeRTOS系列教程之freeRTOS入…

进销存仓库管理系统有哪些?哪些适合商户用?

进销存仓库管理系统可以帮助商家实现准确的库存控制、优化采购和销售活动&#xff0c;提升仓库操作效率&#xff0c;并提供数据分析和决策支持&#xff0c;从而解决企业在库存管理和供应链方面的问题&#xff0c;提升整体运营效率和竞争力。 进销存仓库管理系统有哪些&#xf…

SmartSQL 一款开源的数据库文档管理工具

建议直接蓝奏云下载安装 蓝奏云下载&#xff1a;https://wwoc.lanzoum.com/b04dpvcxe 蓝奏云密码&#xff1a;123 项目介绍 SmartSQL 是一款方便、快捷的数据库文档查询、导出工具&#xff01;从最初仅支持 数据库、CHM文档格式开始&#xff0c;通过不断地探索开发、集思广…

LVGL(72)-v8--滑块slider

一、slider 简介 1.1 概述 Overview Slider对象看起来像一个带有旋钮的工具条。可以拖动该旋钮来设置一个值。滑块也可以是垂直的或水平的。滑动条在前面我们介绍img控件的时候有个历程有使用到&#xff0c;哪里我们讲述设置样式实现对滑动条的一些样式的设置。 1.2 部分和风…

如果是你,会不会修改这段代码?

最近在合流一些功能&#xff0c;然后有一部分功能的代码需要重构。 需要重构的代码如下&#xff0c;没有重构之前是可以正确执行的。 unsigned int Hex2Int (char c) {if (c > 0 && c < 9) {return (unsigned int) (c - 0);} else {return (unsigned int)(c - a1…

excel 单元格内多行文本的行数量统计

excel公式&#xff1a; LEN(H2)-LEN(SUBSTITUTE(H2,CHAR(10),))1

js中如何获取当前页面的URL参数值?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 获取当前页面的URL参数值⭐ 解析查询字符串⭐ 使用正则表达式解析参数值⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&am…

Clion的使用和配置

工欲善其事必先利其器&#xff0c;开发C好用的IDE必须要配置好&#xff0c;下面我们简单介绍一下现代化的编译工具Clion Clion安装 Ubuntu 一般来说在官网下载好后&#xff0c;解压到文件夹会有一个Install-Linux-tar.txt&#xff0c;按照这个教程安装就行 Clion配置 增加…

stable diffused快速入门

一、下载安装 1&#xff0c;网盘n519 2&#xff0c;环境依赖和模型整合包必须下载&#xff0c;各种风格的模型可选 3&#xff0c;解压模型整合包 4&#xff0c;双击启动器运行依赖这个可执行文件&#xff0c;安装一些环境&#xff0c;直接下一步即可 二、启动 1&#xff0…

PMP与ACP的区别是什么?

首先&#xff0c;我们需要了解PMP和ACP的定义。如果你正在犹豫是否应该考取PMP或ACP证书&#xff0c;或者想知道这两种证书的区别和哪一个更有用&#xff0c;那么请继续阅读。 1. PMP是指项目管理专业人士认证&#xff0c;全称为Project Management Professional。在许多企业&a…