【UVa10674】Tangents(两圆公切线的切点--验板子题)

题目地址: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;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值