题目地址:https://vjudge.net/problem/UVA-10674
题目:
https://uva.onlinejudge.org/external/106/10674.pdf
给出两个圆的圆心坐标和半径,求公切线数目(-1表示无穷)、两圆公切线的切点和这条公切线上切点的距离。
ac代码:
注意精度!!比较大小尽量用三态函数
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 10;
const double eps = 1e-6;//eps用于控制精度,或者更高精度
const double pi = acos(-1.0);//pi
struct Point//点或向量
{
double x, y;
Point(double x = 0, double y = 0) :x(x), y(y) {}
friend bool operator < (Point a, Point b)
{
return a.x == b.x ? a.y < b.y : a.x < b.x;
}
};
struct Circle
{
Point c;
double r;
Circle(Point c, double r):c(c),r(r){}
Point point(double a)//基于圆心角求圆上一点坐标
{
return Point(c.x + cos(a)*r, c.y + sin(a)*r);
}
};
typedef Point Vector;
Vector operator + (Vector a, Vector b)//向量加法
{
return Vector(a.x + b.x, a.y + b.y);
}
Vector operator - (Vector a, Vector b)//向量减法
{
return Vector(a.x - b.x, a.y - b.y);
}
Vector operator * (Vector a, double p)//向量数乘
{
return Vector(a.x * p, a.y * p);
}
Vector operator / (Vector a, double p)//向量数除
{
return Vector(a.x / p, a.y / p);
}
int dcmp(double x)//精度三态函数(>0,<0,=0)
{
if (fabs(x) < eps)return 0; //等于
else return x < 0 ? -1 : 1;//小于,大于
}
bool operator == (const Point &a, const Point &b)//向量相等
{
return dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) == 0;
}
double Dot(Vector a, Vector b)//点积
{
return a.x * b.x + a.y * b.y;
}
double Length(Vector a)//模
{
return sqrt(Dot(a, a));
}
//a[i]和b[i]分别表示第i条切线在圆A和圆B上的切点
int getTangents(Circle A, Circle B, Point* a, Point* b)
{
int cnt = 0;
if(dcmp(A.r - B.r) < 0) {swap(A, B);swap(a, b);}
double d2 = (A.c.x - B.c.x)*(A.c.x - B.c.x) + (A.c.y - B.c.y)*(A.c.y - B.c.y); //圆心距离
double rdiff = A.r - B.r;
double rsum = A.r + B.r;
if(dcmp(d2 - rdiff*rdiff) < 0) return 0;//inside
double base = atan2(B.c.y - A.c.y, B.c.x - A.c.x); // AB向量的极角,A位大圆
if(dcmp(d2 - 0) == 0 && dcmp(A.r - B.r) == 0) return -1;//无数条
if(dcmp(d2 - rdiff*rdiff) == 0)//内切
{
a[cnt] = A.point(base); b[cnt] = B.point(base); cnt++; // 左侧相切
return 1;
}
double ang = acos((A.r - B.r) / sqrt(d2));//外公切线的图
a[cnt] = A.point(base + ang); b[cnt] = B.point(base + ang); cnt++;
a[cnt] = A.point(base - ang); b[cnt] = B.point(base - ang); cnt++;
if(dcmp(d2 - rsum * rsum) == 0)//一条内公切线
{
a[cnt] = A.point(base); b[cnt] = B.point(base + pi); cnt++;
}
else if(dcmp(d2 - rsum * rsum) > 0)//两条内公切线,对应内公切线的图
{
double ang = acos((A.r + B.r) / sqrt(d2));
a[cnt] = A.point(base + ang); b[cnt] = B.point(base + ang + pi); cnt++;
a[cnt] = A.point(base - ang); b[cnt] = B.point(base - ang + pi); cnt++;
}
return cnt;
}
struct node{
Point A, B;
double len;
friend bool operator < (node a, node b)
{
return dcmp(a.A.x - b.A.x) == 0 ? a.A.y < b.A.y : a.A.x < b.A.x;
}
};
int main()
{
//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
double x1, y1, r1, x2, y2, r2;
while(scanf("%lf %lf %lf %lf %lf %lf", &x1, &y1, &r1, &x2, &y2, &r2))
{
Point aq[maxn], bq[maxn];
node ans[maxn];
if(!dcmp(x1) && !dcmp(y1) && !dcmp(r1) && !dcmp(x2) && !dcmp(y2) && !dcmp(r2)) break;
Point a = {x1, y1}, b = {x2, y2};
Circle A ={a, r1}, B = {b, r2};
int cnt = getTangents(A, B, aq, bq);
printf("%d\n", cnt);
if(cnt == 0 || cnt == -1) continue;
for(int i = 0; i < cnt; i++)
{
ans[i].A = aq[i];
ans[i].B = bq[i];
ans[i].len = Length(aq[i] - bq[i]);
//printf("i:%d A(%lf, %lf) B(%lf, %lf) len:%lf\n", i, ans[i].A.x, ans[i].A.y, ans[i].B.x, ans[i].B.y, ans[i].len);
}
sort(ans, ans + cnt);
for(int i = 0; i < cnt; i++)
printf("%.5lf %.5lf %.5lf %.5lf %.5lf\n",ans[i].A.x, ans[i].A.y, ans[i].B.x, ans[i].B.y, ans[i].len);
}
return 0;
}