题目地址:https://vjudge.net/problem/UVA-12304

题目:


看这里:https://uva.onlinejudge.org/external/123/12304.pdf

 

解题思路:


紫书P267-P269将的很清楚了,新的知识点是直线平移的求法:

直线,v是方向向量

1.求出单位法向量normal

Point Verunit(Point a) //单位法向量
{
    return Point(-a.y, a.x) / Length(a);
}
Point normal = Verunit(v);

2.借助单位法向量和平移距离d求出直线

new_line = Line(p+normal*d, v)

其中p+noraml*d是p在平移后的直线对应的坐标p’

 

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;
    }
};
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 Cross(Vector a, Vector b)//外积
{
    return a.x * b.y - a.y * b.x;
}
double Length(Vector a)//模
{
    return sqrt(Dot(a, a));
}
Vector Rotate(Vector a, double rad)//逆时针旋转rad弧度
{
    return Vector(a.x*cos(rad) - a.y*sin(rad), a.x*sin(rad) + a.y*cos(rad));
}
double Angle(Point a)//极角
{
    return atan2(a.y, a.x);
}
double RadToDegree(double rad)//弧度转角度
{
    return 180.0*rad/pi;
}
Point Verunit(Point a) //单位法向量
{
    return Point(-a.y, a.x) / Length(a);
}
double DistanceToLine(Point P, Point A, Point B)//点P到AB所在直线的距离
{
    Vector v1 = B - A, v2 = P - A;
    return fabs(Cross(v1, v2)) / Length(v1);
}


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);
    }
};
struct Line{ // p+v*t t是变量
    Point p;
    Point v;//方向向量
    double ang;//斜率
    Line(Point _p, Point _v):p(_p),v(_v){ang = atan2(v.y, v.x);}
    Point point(double a) // 直线上某一点
    {
        return p+v*a;
    }
    friend bool operator < (Line a, Line b)
    {
        return a.ang < b.ang;
    }
};

//两圆交点
int getCirleCirleIntersection(Circle C1, Circle C2, vector<Point>& sol)//求两圆交点
{
    double d = Length(C1.c - C2.c);
    if (dcmp(d) == 0)//圆心重合
    {
        if (dcmp(C1.r - C2.r) == 0) return -1;//重合
        else return 0;
    }
    if (dcmp(C1.r + C2.r - d) < 0) return 0;//相离
    if (dcmp(fabs(C1.r - C2.r) - d) > 0) return 0; //内含,圆心不重合
    
    double a = Angle(C2.c - C1.c);
    double da = acos((C1.r*C1.r + d * d - C2.r*C2.r) / (2 * C1.r*d));
    Point p1 = C1.point(a - da), p2 = C1.point(a + da);
    sol.push_back(p1);
    if (p1 == p2)return 1;
    sol.push_back(p2);
    return 2;
}
// 直线交点,P,方向向量v
Point GetLineIntersection(Point P, Vector v, Point Q, Vector w)
{
    Vector u = P - Q;//QP
    double t = Cross(w, u) / Cross(v, w);
    return P + v * t;
}
//直线和圆的交点
int getLineCircleIntersection(Line L, Circle C, double& t1, double& t2, vector<Point>& sol)
{
    double a = L.v.x, b = L.p.x - C.c.x, c = L.v.y, d = L.p.y - C.c.y;
    double e = a*a + c*c, f = 2*(a*b + c*d), g = b*b + d*d -C.r*C.r;
    double delta = f*f - 4*e*g;//判别式
    if(dcmp(delta) < 0) return 0;//相离
    if(dcmp(delta) == 0)
    {
        t1 = t2 = -f / (2*e);
        sol.push_back(L.point(t1));
        return 1;//相切
    }
    t1 = (-f - sqrt(delta)) / (2*e); sol.push_back(L.point(t1));
    t2 = (-f + sqrt(delta)) / (2*e); sol.push_back(L.point(t2));
    return 2;
}
//外接圆
Circle CircumscribedCircle(Point p1, Point p2, Point p3)
{
    double Bx = p2.x-p1.x, By = p2.y-p1.y;
    double Cx = p3.x-p1.x, Cy = p3.y-p1.y;
    double D = 2*(Bx*Cy-By*Cx);
    double cx = (Cy*(Bx*Bx+By*By) - By*(Cx*Cx+Cy*Cy))/D + p1.x;
    double cy = (Bx*(Cx*Cx+Cy*Cy) - Cx*(Bx*Bx+By*By))/D + p1.y;
    Point p = Point(cx, cy);
    return Circle(p, Length(p1-p));
}
//内切圆
Circle InscribedCircle(Point p1, Point p2, Point p3)
{
    double a = Length(p2-p3);
    double b = Length(p3-p1);
    double c = Length(p1-p2);
    Point p = (p1*a+p2*b+p3*c) / (a+b+c);
    return Circle(p, DistanceToLine(p, p1, p2));
}
//过定点做圆的切线, 返回切点个数,v中存切点
int TangentLineThroughPoint(Point p, Circle C, Vector* v)
{
    Vector u = C.c - p;
    double dist = Length(u);
    if(dist < C.r) return 0;//inside
    else if(dcmp(dist - C.r) == 0)//on the circle
    {
        v[0] = Rotate(u, pi/2);
        return 1;
    }
    else //outside
    {
        double ang = asin(C.r / dist);
        v[0] = Rotate(u, -ang);
        v[1] = Rotate(u, ang);
        return 2;
    }
}
int main()
{
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    char op[200];
    double a[20];
    Point v[10];//存直线和圆的切点
    double degree[10];//存极角
    vector<Point> sol;//存坐标
    while(~scanf("%s", op))
    {
        if(strcmp(op, "CircumscribedCircle") == 0)
        {
            for(int i = 1; i <= 6; i++) scanf("%lf", &a[i]);
            Circle C = CircumscribedCircle(Point(a[1], a[2]), Point(a[3], a[4]), Point(a[5], a[6]));
            printf("(%.6lf,%.6lf,%.6lf)\n", C.c.x, C.c.y, C.r);
        }
        else if(strcmp(op, "InscribedCircle") == 0)
        {
            for(int i = 1; i <= 6; i++) scanf("%lf", &a[i]);
            Circle C = InscribedCircle(Point(a[1], a[2]), Point(a[3], a[4]), Point(a[5], a[6]));
            printf("(%.6lf,%.6lf,%.6lf)\n", C.c.x, C.c.y, C.r);
        }
        else if(strcmp(op, "TangentLineThroughPoint") == 0)
        {
            for(int i = 1; i <= 5; i++) scanf("%lf", &a[i]);
            int num = TangentLineThroughPoint(Point(a[4], a[5]), Circle(Point(a[1], a[2]), a[3]), v);
            if(!num)
            {
                printf("[]\n");
                continue;
            }
            for(int i = 0; i < num; i++)
            {
                double de = RadToDegree(Angle(v[i]));//极角可能是负的,不符合条件的情况:de<0,de>=180
                if(dcmp(de) < 0) de = 180.0 + de; // de < 0
                else while(dcmp(de-180.0) >= 0) de -= 180.0; // de >= 180
                degree[i] = de;
            }
            sort(degree, degree+num);
            printf("[");
            for(int i = 0; i < num; i++)
            {
                if(i) printf(",");
                printf("%.6lf", degree[i]);
            }
            printf("]\n");
        }
        else if(strcmp(op, "CircleThroughAPointAndTangentToALineWithRadius") == 0)
        {
            for(int i = 1; i <= 7; i++) scanf("%lf", &a[i]);
            Point A = Point(a[3], a[4]), B = Point(a[5], a[6]);
            Circle C = Circle(Point(a[1], a[2]), a[7]);
            Point normal = Verunit(B-A) * a[7];
            //获得和BA这条直线平行且距离为r的直线
            Line l1 = Line(A+normal, B-A), l2 = Line(A-normal, B-A);
            //转化为两直线和圆的交点
            sol.clear();
            double t1 = -1,t2 = -1;//两交点的极角
            getLineCircleIntersection(l1,C,t1,t2,sol);
            getLineCircleIntersection(l2,C,t1,t2,sol);
            sort(sol.begin(), sol.end());
            printf("[");
            for(int i = 0; i < sol.size(); i++)
            {
                if(i) printf(",");
                printf("(%.6lf,%.6lf)", sol[i].x, sol[i].y);
            }
            printf("]\n");
        }
        else if(strcmp(op, "CircleTangentToTwoLinesWithRadius") == 0)
        {
            for(int i = 1; i <= 9; i++) scanf("%lf", &a[i]);
            Point A = Point(a[1],a[2]), B = Point(a[3],a[4]);
            Point C = Point(a[5],a[6]), D = Point(a[7], a[8]);
            Point normal1 = Verunit(B-A) * a[9];
            Point normal2 = Verunit(D-C) * a[9];
            //转化为求两两直线交点,已经保证必有交点
            sol.clear();
            sol.push_back(GetLineIntersection(A+normal1, B-A, C+normal2, D-C));
            sol.push_back(GetLineIntersection(A+normal1, B-A, C-normal2, D-C));
            sol.push_back(GetLineIntersection(A-normal1, B-A, C+normal2, D-C));
            sol.push_back(GetLineIntersection(A-normal1, B-A, C-normal2, D-C));
            sort(sol.begin(), sol.end());
            printf("[");
            for(int i = 0; i < sol.size(); i++)
            {
                if(i) printf(",");
                printf("(%.6lf,%.6lf)",sol[i].x, sol[i].y);
            }
            printf("]\n");
        }
        else if(strcmp(op, "CircleTangentToTwoDisjointCirclesWithRadius") == 0)
        {
            for(int i = 1; i <= 7; i++) scanf("%lf", &a[i]);
            Circle c1 = Circle(Point(a[1],a[2]),a[3]+a[7]);
            Circle c2 = Circle(Point(a[4],a[5]),a[6]+a[7]);
            //转化为两圆交点
            sol.clear();
            getCirleCirleIntersection(c1, c2, sol);
            sort(sol.begin(), sol.end());
            printf("[");
            for(int i = 0; i < sol.size(); i++)
            {
                if(i) printf(",");
                printf("(%.6lf,%.6lf)",sol[i].x, sol[i].y);
            }
            printf("]\n");
        }
    }
    return 0;
}
/**
output:
(9.734940,5.801205,11.332389)
(9.113006,6.107686,5.644984)
[53.977231,160.730818]
[0.000000]
[]
[(112.047575,299.271627),(199.997744,199.328253)]
[(-0.071352,123.937211),(150.071352,256.062789)]
[(100.000000,200.000000)]
[]
[(72.231286,121.451368),(87.815122,63.011983),(128.242785,144.270867),(143.826621,85.831483)]
[(157.131525,134.836744),(194.943947,202.899105)]
[(204.000000,178.000000)]
 */