计算几何 - Tangents - UVA 10674

计算几何 - Tangents - UVA 10674

题意:

给 顶 两 个 圆 的 圆 心 , 半 径 , 求 出 这 两 个 圆 的 所 有 公 切 线 。 给顶两个圆的圆心,半径,求出这两个圆的所有公切线。 线

输入:

多 组 测 试 数 据 , 多组测试数据,

六 个 整 数 , x 1 , y 1 , r 1 , x 2 , y 2 , r 2 , 分 别 表 示 第 一 个 圆 和 第 二 个 圆 的 横 、 纵 坐 标 , 半 径 。 六个整数,x_1,y_1,r_1,x_2,y_2,r_2,分别表示第一个圆和第二个圆的横、纵坐标,半径。 x1,y1,r1,x2,y2,r2

输出:

首 行 一 个 整 数 , 表 示 切 线 的 条 数 。 首行一个整数,表示切线的条数。 线

若 有 无 数 条 切 线 ( 两 圆 重 合 ) , 输 出 − 1 。 若有无数条切线(两圆重合),输出-1。 线()1

五 个 浮 点 数 S x , S y , T x , T y , L , 分 别 表 示 切 线 在 第 一 个 圆 上 和 第 二 个 圆 上 的 切 点 的 横 纵 坐 标 , 最 后 一 个 数 表 示 切 线 长 。 五个浮点数S_x,S_y,T_x,T_y,L,分别表示切线在第一个圆上和第二个圆上的切点的横纵坐标,\\最后一个数表示切线长。 Sx,Sy,Tx,Ty,L线线

以 S x 为 第 一 关 键 字 , S y 为 第 二 关 键 字 , 将 结 果 升 序 排 序 后 输 出 。 以S_x为第一关键字,S_y为第二关键字,将结果升序排序后输出。 SxSy

Sample Input

10 10 5 20 20 5
10 10 10 20 20 10
10 10 5 20 10 5
0 0 0 0 0 0

Sample Output

4
6.46447 13.53553 16.46447 23.53553 14.14214
10.00000 15.00000 20.00000 15.00000 10.00000
13.53553 6.46447 23.53553 16.46447 14.14214
15.00000 10.00000 15.00000 20.00000 10.00000
2
2.92893 17.07107 12.92893 27.07107 14.14214
17.07107 2.92893 27.07107 12.92893 14.14214
3
10.00000 5.00000 20.00000 5.00000 10.00000
10.00000 15.00000 20.00000 15.00000 10.00000
15.00000 10.00000 15.00000 10.00000 0.00000

数据范围:

− 100 ≤ x 1 , y 1 , x 2 , y 2 ≤ 100 , 0 < r 1 , r 2 ≤ 200. −100 ≤ x_1,y_1,x_2,y_2 ≤ 100, 0 < r_1,r_2≤ 200. 100x1,y1,x2,y2100,0<r1,r2200.


注意:

题 中 虽 然 说 输 入 的 都 是 整 数 , 但 是 还 是 要 以 浮 点 数 的 形 式 读 入 , 否 则 就 会 疯 狂 W A ( 为 什 么 ? ? ? ? ) 题中虽然说输入的都是整数,但是还是要以浮点数的形式读入,否则就会疯狂WA(为什么????) WA()

为 了 输 出 , 可 以 写 一 个 点 对 结 构 体 重 载 一 下 小 于 号 。 为了输出,可以写一个点对结构体重载一下小于号。

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<vector>
#include<algorithm>

using namespace std;

const double eps=1e-10;
const double pi=acos(-1.0);

struct Point 
{
    double x,y;
    int id;
    Point(double x=0,double y=0) : x(x), y(y) {}
};

//点与向量
typedef Point Vector;

Vector operator + (Vector A,Vector B) { return Vector(A.x+B.x,A.y+B.y); }

Vector operator - (Point A,Point 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)
{
    if(fabs(x)<eps) return 0;
    else return x<0 ? -1 : 1;
}

bool operator < (const Point &a,const Point &b)
{
    return a.x<b.x || (!dcmp(a.x-b.x) && a.y<b.y);
}

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)); }

double Angle(Vector A, Vector B) { return acos(Dot(A,B) / Length(A) / Length(B)); } //A和B夹角

double Cross(Vector A, Vector B) { return A.x*B.y - A.y*B.x; }  //若A到B逆时针则为正,否则为负

double Area2(Point A,Point B,Point C) { return Cross(B-A,C-A); }    //三角形ABC的面积的两倍(有方向)

double angle(Vector v) { return atan2(v.y,v.x); }   //arctan(y/x)

double dis2(Point A,Point B) { return (A.x-B.x)*(A.x-B.x) + (A.y-B.y)*(A.y-B.y); }

//圆
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);
    }
}; 

//求两圆的公切线
//返回切线的条数,-1表示无穷条切线(两圆重合)
// a[i]和b[i]分别表示第i条切线在圆A和圆B上的切点
int getTangents(Circle A,Circle B,Point *a,Point *b)
{
    int cnt=0;
    if(A.r<B.r) { swap(A,B); swap(a,b); }
    double d2=dis2(A.c,B.c);
    double rdiff=A.r-B.r;
    double rsum=A.r+B.r;
    if(d2<rdiff*rdiff) return 0;    //内含
    
    double base=atan2(B.c.y-A.c.y,B.c.x-A.c.x);
    if(!dcmp(d2) && !dcmp(A.r-B.r)) return -1;    //重合
    if(!dcmp(d2-rdiff*rdiff))     //内切,1条切线
    {
        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))
    {
        a[cnt]=A.point(base); 
        b[cnt]=B.point(pi+base); 
        cnt++;
    }
    else if(d2>rsum*rsum)
    {
        double ang=acos((A.r+B.r)/sqrt(d2));
        a[cnt]=A.point(base+ang); b[cnt]=B.point(pi+base+ang); cnt++;
        a[cnt]=A.point(base-ang); b[cnt]=B.point(pi+base-ang); cnt++;
    }
    
    return cnt;
}

struct Point_pair
{
    Point A,B;
    bool operator < (const Point_pair &t) const
    {
        if(dcmp(t.A.x-A.x)==0) return A.y<t.A.y;
        return A.x<t.A.x;
    }
};

int main()
{
    double a,b,r1,c,d,r2;
    while(~scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&r1,&c,&d,&r2))
    {
        if(!dcmp(a)&&!dcmp(b)&&!dcmp(r1)&&!dcmp(c)&&!dcmp(d)&&!dcmp(r2)) break;
        Point O1=Point(a,b), O2=Point(c,d);
        Circle A=Circle(O1,r1), B=Circle(O2,r2);
        Point u[20], v[20];
        Point_pair p[20];
        int t=getTangents(A,B,u,v);
        for(int i=0;i<t;i++) p[i].A=u[i], p[i].B=v[i];
        if(t>0) sort(p,p+t);
        
        printf("%d\n",t);
        for(int i=0;i<t;i++) printf("%.5lf %.5lf %.5lf %.5lf %.5lf\n",
                                p[i].A.x,p[i].A.y,p[i].B.x,p[i].B.y,Length(p[i].A-p[i].B));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
㈠ 点的基本运算 1. 平面上两点之间距离 1 2. 判断两点是否重合 1 3. 矢量叉乘 1 4. 矢量点乘 2 5. 判断点是否在线段上 2 6. 求一点饶某点旋转后的坐标 2 7. 求矢量夹角 2 ㈡ 线段及直线的基本运算 1. 点与线段的关系 3 2. 求点到线段所在直线垂线的垂足 4 3. 点到线段的最近点 4 4. 点到线段所在直线的距离 4 5. 点到折线集的最近距离 4 6. 判断圆是否在多边形内 5 7. 求矢量夹角余弦 5 8. 求线段之间的夹角 5 9. 判断线段是否相交 6 10.判断线段是否相交但不交在端点处 6 11.求线段所在直线的方程 6 12.求直线的斜率 7 13.求直线的倾斜角 7 14.求点关于某直线的对称点 7 15.判断两条直线是否相交及求直线交点 7 16.判断线段是否相交,如果相交返回交点 7 ㈢ 多边形常用算法模块 1. 判断多边形是否简单多边形 8 2. 检查多边形顶点的凸凹性 9 3. 判断多边形是否凸多边形 9 4. 求多边形面积 9 5. 判断多边形顶点的排列方向,方法一 10 6. 判断多边形顶点的排列方向,方法二 10 7. 射线法判断点是否在多边形内 10 8. 判断点是否在凸多边形内 11 9. 寻找点集的graham算法 12 10.寻找点集凸包的卷包裹法 13 11.判断线段是否在多边形内 14 12.求简单多边形的重心 15 13.求凸多边形的重心 17 14.求肯定在给定多边形内的一个点 17 15.求从多边形外一点出发到该多边形的切线 18 16.判断多边形的核是否存在 19 ㈣ 圆的基本运算 1 .点是否在圆内 20 2 .求不共线的三点所确定的圆 21 ㈤ 矩形的基本运算 1.已知矩形三点坐标,求第4点坐标 22 ㈥ 常用算法的描述 22 ㈦ 补充 1.两圆关系: 24 2.判断圆是否在矩形内: 24 3.点到平面的距离: 25 4.点是否在直线同侧: 25 5.镜面反射线: 25 6.矩形包含: 26 7.两圆交点: 27 8.两圆公共面积: 28 9. 圆和直线关系: 29 10. 内切圆: 30 11. 求切点: 31 12. 线段的左右旋: 31

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值