UVA-12304 2D Geometry 110 in 1! (有关圆的基本操作)
Posted 1625--h
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UVA-12304 2D Geometry 110 in 1! (有关圆的基本操作)相关的知识,希望对你有一定的参考价值。
UVA-12304 2D Geometry 110 in 1!
该问题包含以下几个子问题
-
CircumscribedCircle x1 y1 x2 y2 x3 y3 : 三角形外接圆
-
InscribedCircle x1 y1 x2 y2 x3 y3: 三角形内接圆
-
TangentLineThroughPoint xc yc r xp yp 过一点做圆的切线
-
CircleThroughAPointAndTangentToALineWithRadius xp yp x1 y1 x2 y2 r:找到半径为r,通过p点,并且与直线L相切的圆
-
CircleTangentToTwoLinesWithRadius x1 y1 x2 y2 x3 y3 x4 y4 r:与两条直线相切,并且半径为r
-
CircleTangentToTwoDisjointCirclesWithRadius x1 y1 r1 x2 y2 r2 r:求出所有与这两个圆外切,半径为r的圆
下面代码很多都是在模板基础上面写的:几何模板
1. 求三角形的外接圆
根据初中知识,三边垂直平分线的交点就是圆心,求出圆心很容易可以求出半径,所以现在需要求一个线段的垂直平分线,以及两条线段的交点。线段的中点可以用两端点取平均来求,垂直平分线可以用将向量(vec{AB}) 转90度得到向量,在线段中点基础上就可以得到垂直平分线了。
// circle 结构体的构造函数
circle(Point a, Point b, Point c){
Line u = Line((a+b)/2,((a+b)/2)+((b-a).rotleft()));
Line v = Line((b+c)/2,((b+c)/2)+((c-b).rotleft()));
p = u.crosspoint(v); // p 为 u 与 v 的交点
r = p.distance(a);
}
2. 求三角形的内接圆
circle(Point a, Point b, Point c, bool t){
Line u, v;
// u 为角 a 的平分线, m 为ab向量极角,n为ac向量极角,取平均得到角平分线的极角
db m = atan2(b.y-a.y, b.x-a.x), n = atan2(c.y - a.y, c.x - a.x);
u.s = a;
u.e = u.s + Point(cos((n+m)/2), sin((n+m)/2)); // u.s + 角平分线单位向量得到角平分线
// v 为角 b 的平分线
m = atan2(a.y-b.y, a.x-b.x), n = atan2(c.y-b.y, c.x-b.x);
v.s = b;
v.e = v.s + Point(cos((n+m)/2), sin((n+m)/2));
p = u.crosspoint(v); // 得到圆心
r = Line(a,b).dispointtoseg(p);
}
3. 过一点做圆的切线
首先判断点与圆的关系
- 若点在圆内,则没有通过该点的切线d
- 若点在圆上,可以通过将圆心连向它的线段转90度得到切线
- 若点在圆外,如下图所示,我们可以计算出(ang CAB) ,进而计算出(|vec{AD}|,|vec{DC}|),然后在A的基础上加上这两个向量得到C,进而得到一条切线,下面的切点可以用同样的方法得到。
/*
点和圆的关系
0 圆外
1 圆上
2 圆内
*/
int relation(Point b){
db dst = b.distance(p);
if(sgn(dst - r) < 0) return 2;
else if(sgn(dst - r) == 0) return 1;
return 0;
}
// 过一点作圆的切线 (先判断点和圆的关系)
int tangentline(Point q, Line &u, Line &v){
int x = relation(q);
if(x == 2) return 0; //圆内
if(x == 1){ //圆上
u = Line(q, q+(q-p).rotleft());
v = u;
return 1;
}
db d = p.distance(q); // 得到AB向量的大小
db l = r * r / d; // 通过余弦定理,得到 AD向量大小
db h = sqrt(r * r - l * l); // 通过勾股定理求出DC向量大小
// vec.trunc(r) 表示将vec向量大小调整为r, rotleft表示逆时针旋转90度
u = Line(q, p + ((q - p).trunc(l) + (q - p).rotleft().trunc(h)));
v = Line(q, p + (q - p).trunc(l) + (q - p).rotright().trunc(h));
return 2;
}
4. 找到半径为r,通过p点,并且与直线L相切的圆
先讨论 p 点与 L 的距离 dis
-
若(dis gt 2*r) ,则没有这样的圆
-
若(dis = 0) ,则p 点在直线上,利用前面提到的方法,得到一个长度为 r 的向量,并且与直线夹角为90度,与 p 相加后可以得到圆心。(这样的圆心有两个)
-
其他情况可以见下图:
红线为L,绿线为平行线,红线与绿线的长度为 r,以 p 为圆心,r为半径做圆,与平行线交于两点(只有可能是两个点),这两点就是符合题意的圆的圆心
// 得到与直线 u 相切,过点 q, 半径为 r1 的圆
int getcircle(Line u, Point q, db r1, circle &c1, circle &c2){
db dis = u.dispointtoline(q); // 直线 u 与 q 的距离
if(sgn(dis - r1 * 2) > 0) return 0;// dis > r1 * 2
if(sgn(dis) == 0){ // q 在 u 上面
c1.p = q + ((u.e - u.s).rotleft().trunc(r1));
c2.p = q + ((u.e - u.s).rotright().trunc(r1));
c1.r = c2.r = r1;
return 2;
}
// u1, u2 为两条平行线
Line u1 = Line((u.s + (u.e - u.s).rotleft().trunc(r1)), (u.e + (u.e - u.s).rotleft().trunc(r1)));
Line u2 = Line((u.s + (u.e - u.s).rotright().trunc(r1)), (u.e + (u.e - u.s).rotright().trunc(r1)));
circle cc = circle(q, r1);
Point p1, p2;
// cc 与 u1,u2 两条线中的一个相交
if(!cc.pointcrossline(u1, p1, p2)) cc.pointcrossline(u2, p1, p2);
c1 = circle(p1, r1);
if(p1 == p2){ // 可能两个圆是重合的,这个对应 dis = 2*ri 的情况
c2 = c1;
return 1;
}
c2 = circle(p2, r1);
return 2;
}
5. 与两条直线l1,l2相切,并且半径为r
题目保证了两条直线不是相交的,但是不妨思考一下,如果是平行的话,只有可能是 0 或者是无限个
对于不相交的情况,先上图再说(红色为l1和l2,蓝色为平行线)
相信你一看图就明白了,就是找到两个直线的平行线求交点,这样的交点一定有四个。
// 同时与直线u,v相切,半径为r1的圆
int getcircle(Line u, Line v, db r1, circle &c1, circle &c2, circle &c3, circle &c4){
if(u.parallel(v)) return 0;
Line u1 = Line(u.s + (u.e - u.s).rotleft().trunc(r1), u.e + (u.e - u.s).rotleft().trunc(r1));
Line u2 = Line(u.s + (u.e - u.s).rotright().trunc(r1), u.e + (u.e - u.s).rotright().trunc(r1));
Line v1 = Line(v.s + (v.e - v.s).rotleft().trunc(r1), v.e + (v.e - v.s).rotleft().trunc(r1));
Line v2 = Line(v.s + (v.e - v.s).rotright().trunc(r1), v.e + (v.e - v.s).rotright().trunc(r1));
c1.r = c2.r = c3.r = c4.r = r1;
c1.p = u1.crosspoint(v1);
c2.p = u1.crosspoint(v2);
c3.p = u2.crosspoint(v1);
c4.p = u2.crosspoint(v2);
return 4;
}
6. 求出所有与两个圆c1, c2外切,半径为r的圆
将 c1 与 c2 半径都扩大 r,求扩大的两个圆的交点即可。如何求圆的交点?
三角形( riangle ABE) 三边都是确定的,由余弦定理求出(ang alpha) 的角度,然后来求出 E
/*
两圆的关系
5 相离
4 外切
3 相交
2 内切
1 内含
*/
int relationcircle(circle v){
db d = p.distance(v.p);
if(sgn(d - r - v.r) > 0) return 5;
if(sgn(d - r - v.r) == 0) return 4;
db l = fabs(r - v.r);
if(sgn(d - r - v.r) < 0 && sgn(d - l) > 0) return 3;
if(sgn(d - l) == 0) return 2;
if(sgn(d - l) < 0) return 1;
}
/*
求两个圆的交点,返回0表示没有交点,返回1是一个交点,2是两个交点
*/
int pointcrosscircle(circle v, Point &p1, Point &p2){
int rel = relationcircle(v);
// 相离或者内含
if(rel == 1 || rel == 5) return 0;
// d 为圆心距,下面求E的方法类似问题3
db d = p.distance(v.p);
db l = (d * d + r * r - v.r * v.r) / (2 * d);
db h = sqrt(r * r - l * l);
Point tmp = p + (v.p - p).trunc(l);
p1 = tmp + ((v.p - p).rotleft().trunc(h));
p2 = tmp + ((v.p - p).rotright().trunc(h));
if(rel == 2 || rel == 4)return 1;
return 2;
}
// 同时与不相交圆 cx, cy 相切,半径为r1的圆
int getcircle(circle cx, circle cy, db r1, circle &c1, circle &c2){
// 得到两个更大的圆
circle x(cx.p, r1+cx.r), y(cy.p, r1+cy.r);
// 求两个圆的交点
int t = x.pointcrosscircle(y, c1.p, c2.p);
if(!t) return 0;
c1.r = c2.r = r1;
return t;
}
AC代码
#include <cstdio>
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
#define dbg(x...) do { cout << "