HDU 6354(计算几何)

传送门

题面:

Everything Has Changed

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 244    Accepted Submission(s): 125
Special Judge

Problem Description

Edward is a worker for Aluminum Cyclic Machinery. His work is operating mechanical arms to cut out designed models. Here is a brief introduction of his work.
Assume the operating plane as a two-dimensional coordinate system. At first, there is a disc with center coordinates (0,0) and radius R. Then, m mechanical arms will cut and erase everything within its area of influence simultaneously, the i-th area of which is a circle with center coordinates (xi,yi) and radius ri (i=1,2,⋯,m). In order to obtain considerable models, it is guaranteed that every two cutting areas have no intersection and no cutting area contains the whole disc.
Your task is to determine the perimeter of the remaining area of the disc excluding internal perimeter.
Here is an illustration of the sample, in which the red curve is counted but the green curve is not.

Input

The first line contains one integer T, indicating the number of test cases.
The following lines describe all the test cases. For each test case:
The first line contains two integers m and R.
The i-th line of the following m lines contains three integers xi,yi and ri, indicating a cutting area.
1≤T≤1000, 1≤m≤100, −1000≤xi,yi≤1000, 1≤R,ri≤1000 (i=1,2,⋯,m).

Output

For each test case, print the perimeter of the remaining area in one line. Your answer is considered correct if its absolute or relative error does not exceed 10−6.
Formally, let your answer be a and the jury's answer be b. Your answer is considered correct if |a−b|max(1,|b|)≤10−6.

Sample Input

1 4 10 6 3 5 10 -4 3 -2 -4 4 0 9 1

Sample Output

81.62198908430238475376

Source

2018 Multi-University Training Contest 5

题目描述:

    给你一个原点在(0,0),半径为R的圆盘以及m个小圆。第i个小圆的原点在(xi,yi),半径为ri。每个小圆可能会与大圆盘相交,现在问你m个小圆将大圆覆盖后的周长并。(题目保证小圆不会相交,且小圆不会完全把大圆覆盖)

题目分析:

    虽然说一眼看上去,这道题貌似是一个挺复杂的计算几何的问题,但是仔细分析一下,其实这个题就是一个简单的高中数学的平面几何的知识。我们知道,如果两圆相交,如下图

    更新后的答案=答案-黑色的那段弧+红色的弧长。

    而两端的弧长可以通过公式 求出。现在我们则需要求出两个圆心角即可。而我们发现,两个圆心角分别可以用余弦公式求出,其中大圆的圆心角的一半的余弦为,每个小圆的圆心角的余弦的一半为,继而我们即可将问题转化为一个解三角形的问题。

    Ps:科普一个纯计算几何的方法。我们可以不解三角形,我们可以用计算几何的方法直接将两个圆的交点求出,求出交点之后,我们在根据余弦定理求出两段弧长,进而进行求解。但是这个做法代码量相当冗长!!因此,能够解三角形解决的,就老老实实解三角形吧。

代码1:解三角形:

#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
const double eps=1e-8;
const double pi=acos(-1);
int sgn(double x){
    if(fabs(x)<eps) return 0;
    if(x<eps) return -1;
    else return 1;
}
struct Point{
    double x,y;
    Point(){}
    Point(double _x,double _y){
        x=_x,y=_y;
    }
    Point operator -(const Point &b)const{
        return Point(x-b.x,y-b.y);
    }
    double operator *(const Point &b)const{
        return x*b.x+y*b.y;
    }
};
double distancePoint(Point a,Point b){//求两点间的距离
    return sqrt((a-b)*(a-b));
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        double R;
        scanf("%d%lf",&n,&R);
        Point o1=Point(0,0);
        double res=2*pi*R;
        for(int i=1;i<=n;i++){
            double x1,y1,r;
            scanf("%lf%lf%lf",&x1,&y1,&r);
            Point o2=Point(x1,y1);
            double o1o2=distancePoint(o1,o2);
            if(sgn(o1o2-(r+R))>=0||sgn(o1o2-(R-r))<0) continue;//内含、外切和不相交的情况忽略
            if(sgn(o1o2-(R-r))==0){
                res+=2*pi*r;
                continue;
            }
            double xita1=(R*R+o1o2*o1o2-r*r)/(2*R*o1o2);//余弦距离求两个圆心角
            double xita2=(r*r+o1o2*o1o2-R*R)/(2*r*o1o2);
            xita1=2*acos(xita1);
            xita2=2*acos(xita2);
            res+=r*xita2;
            res-=R*xita1;
        }
        printf("%.8f\n",res);
    }
}

代码2:纯计算几何模板算法(代码相当冗长):

#include<bits/stdc++.h>
using namespace std;

const double Pi=acos(-1);
const double eps = 1e-6;

struct Point{
    double x,y;
    Point(double tx=0,double ty=0):x(tx),y(ty){}
};
typedef Point Vctor;
int sgn(double x){
    if(fabs(x)<eps) return 0;
    if(x<eps) return -1;
    else return 1;
}
Vctor operator + (Vctor A,Vctor B){return Vctor(A.x+B.x,A.y+B.y);}
Vctor operator - (Point A,Point B){return Vctor(A.x-B.x,A.y-B.y);}
Vctor operator * (Vctor A,double p){return Vctor(A.x*p,A.y*p);}
Vctor operator / (Vctor A,double p){return Vctor(A.x/p,A.y/p);}
bool operator < (Point A,Point B){return A.x < B.x || (A.x == B.x && A.y < B.y);}

struct Line{
    Point p;
    Vctor v;
    Line(Point p=Point(0,0),Vctor v=Vctor(0,0)):p(p),v(v){}
    Point point(double t){return p + v*t;}
};
struct Circle{
    Point c;
    double r;
    Circle(Point tc=Point(0,0),double tr=0):c(tc),r(tr){}
    Point point(double a){return Point( c.x + cos(a)*r , c.y + sin(a)*r);}
};

int dcmp(double x)
{
    if(fabs(x)<eps) return 0;
    else return (x<0)?(-1):(1);
}
bool operator == (Point A,Point B){return dcmp(A.x-B.x)==0 && dcmp(A.y-B.y)==0;}

double Dot(Vctor A,Vctor B){return A.x*B.x+A.y*B.y;}
double Length(Vctor A){return sqrt(Dot(A,A));}
double Angle(Vctor A,Vctor B){return acos(Dot(A,B)/Length(A)/Length(B));}

double Cross(Vctor A,Vctor B){return A.x*B.y-A.y*B.x;}
double TriangleArea(Point A,Point B,Point C){return Cross(B-A,C-A);}

Vctor Rotate(Vctor A,double rad){return Vctor( A.x*cos(rad) - A.y*sin(rad) , A.x*sin(rad) + A.y*cos(rad) );}
Vctor Normal(Vctor A)
{
    double L = Length(A);
    return Vctor(-A.y/L, A.x/L);
}
double distancePoint(Point a,Point b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

Point getLineIntersection(Line L1,Line L2)
{
    Vctor u = L1.p-L2.p;
    double t = Cross(L2.v,u)/Cross(L1.v,L2.v);
    return L1.p + L1.v*t;
}

double DistanceToLine(Point P,Line L)
{
    return fabs(Cross(P-L.p,L.v))/Length(L.v);
}

double DistanceToSegment(Point P,Point A,Point B)
{
    if(A==B) return Length(P-A);
    Vctor v1 = B-A, v2 = P-A, v3 = P-B;
    if (dcmp(Dot(v1,v2)) < 0) return Length(v2);
    else if (dcmp(Dot(v1,v3)) > 0) return Length(v3);
    else return fabs(Cross(v1,v2))/Length(v1);
}

Point getLineProjection(Point P,Line L)
{
    return L.v + L.v*Dot(L.v,P-L.p)/Dot(L.v,L.v);
}

bool SegmentProperIntersection(Point a1,Point a2,Point b1,Point b2)
{
    double c1 = Cross(a2 - a1,b1 - a1), c2 = Cross(a2 - a1,b2 - a1),
           c3 = Cross(b2 - b1,a1 - b1), c4 = Cross(b2 - b1,a2 - b1);
    return dcmp(c1)*dcmp(c2)<0 && dcmp(c3)*dcmp(c4)<0;
}


bool OnSegment(Point P,Point a1,Point a2)
{
    return dcmp(Cross(a1 - P,a2 - P))==0 && dcmp(Dot(a1 - P,a2 - P))<0;
}


double PolgonArea(Point *p,int n)
{
    double area=0;
    for(int i=1;i<n-1;i++) area += Cross( p[i]-p[0] , p[i + 1]-p[0] );
    return area/2;
}


int getLineCircleIntersection(Line L,Circle C,vector<Point> &sol)
{
    double t1,t2;
    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.0*e*g;
    if(dcmp(delta)<0) return 0;
    else if(dcmp(delta)==0)
    {
        t1 = t2 = -f/(2.0*e);
        sol.push_back(L.point(t1));
        return 1;
    }
    else
    {
        t1 = (-f-sqrt(delta))/(2.0*e); sol.push_back(L.point(t1));
        t2 = (-f+sqrt(delta))/(2.0*e); sol.push_back(L.point(t2));
        return 2;
    }
}

//
double angle(Vctor v){return atan2(v.y,v.x);}
int getCircleIntersection(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;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        double R;
        scanf("%d%lf",&n,&R);
        double ans=2*Pi*R;
        Circle yuandian=Circle(Point(0,0),R);
        for(int i=0;i<n;i++){
            double x1,y1,r;
            scanf("%lf%lf%lf",&x1,&y1,&r);
            Circle now=Circle(Point(x1,y1),r);
            vector<Point> res;
            res.clear();
            if(getCircleIntersection(yuandian,now,res)==1){

                if(sgn(distancePoint(Point(x1,y1),Point(0,0))-(r+R))!=0){
                    double tmp=2*Pi*r;
                    ans+=tmp;
                }
            }
            else if(getCircleIntersection(yuandian,now,res)==2){
                //cout<<ans<<endl;
                double zhongjian=distancePoint(res[0],res[1]);
                //cout<<zhongjian<<endl;
                double xita1=((R*R+R*R-zhongjian*zhongjian)/(2*R*R));
                double xita2=((r*r+r*r-zhongjian*zhongjian)/(2*r*r));

                double tmp=sqrt(r*r-(zhongjian/2)*(zhongjian/2));
                double tmp2=sqrt(R*R-(zhongjian/2)*(zhongjian/2));

                xita1=acos(xita1);xita2=acos(xita2);

                if (sgn(tmp-tmp2-distancePoint(Point(x1,y1),Point(0,0)))==0) xita1=2*Pi-xita1;
                if (sgn(tmp2-distancePoint(Point(x1,y1),Point(0,0))-tmp)==0) xita2=2*Pi-xita2;

                ans+=r*xita2;ans-=R*xita1;
             //   cout<<ans<<endl;
            }
        }
        printf("%.8f\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_39453270/article/details/81460523
今日推荐