[codevs 3273] 两圆的交

题解

需要考虑几种情况:

  1. 外切或外离。面积为0,注意要输出 0.000。
  2. 内切或内含或重合。面积为较小圆的面积。
  3. 相交,还需要讨论交点位置:
    • 交点在两圆心中间 即异侧
    • 交点在两圆心同侧

在求三角形面积的时候有两种方法:

  1. 运用三角形两边的叉积的绝对值的1/2计算。
  2. 运用海伦公式计算。

不过我试了所有方法仍然有一个点WA了,已经用while(1)实验出问题是出在交点在两圆心同侧的情况了。可能是精度问题。

后来学到了一种更为简单的方法,第二种代码给出的。推导见注释。

代码:

2ms,256kB
90 分 (WA一点)

#include<cstdio>
#include<iostream>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;

struct Point {
    double x, y;
    Point(double x=0, double y=0):x(x),y(y) {}
}p1, p2;

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

bool operator < (const Vector& a, const Vector& b) {
    return a.x < b.x || (a.x == b.x && a.y < b.y);
}

const double eps = 1e-10;
int dcmp(double x) {
    if(fabs(x) < eps) return 0; else return x < 0 ? -1 : 1;
}

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)); }
double Cross(Vector A, Vector B) { return A.x*B.y - A.y*B.x; }
double Area2(Point A, Point B, Point C) { return Cross(B-A, C-A); }

bool operator == (const Vector& a, const Vector& b) {
    return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;
}

const double PI = acos(double(-1));

struct Circle {
    Point c;
    double r;
    Point point(double a) {
        return Point(c.x + cos(a)*r, c.y + sin(a)*r);
    }
    double S() { return PI*r*r; }
}C1, C2;

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

int getCircleCircleIntersection() {
    double d = Length(C1.c - C2.c);
    if(dcmp(d) == 0) {
        if(dcmp(C1.r-C2.r) == 0) return -1;
        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));

    p1 = C1.point(a-da), p2 = C1.point(a+da);
    return p1 == p2 ? 1 : 2;
}

double Area(double a, double b, double c) {
    double p = (a+b+c) / 2.0;
    return sqrt(p*(p-a)*(p-b)*(p-c));
}

int main() {
    cin >> C1.c.x >> C1.c.y >> C1.r >> C2.c.x >> C2.c.y >> C2.r;

    int c = getCircleCircleIntersection();
    double d = sqrt((C1.c.x-C2.c.x)*(C1.c.x-C2.c.x) + (C1.c.y-C2.c.y)*(C1.c.y-C2.c.y));

    //外离或外切 
    if(d >= C1.r+C2.r) { printf("0.000\n"); return 0; }

    //内含或内切或重合
    if(dcmp(max(C1.r, C2.r)-min(C1.r, C2.r)-d) >= 0) {
        printf("%.3lf\n", min(C1.r, C2.r)*min(C1.r, C2.r)*PI);
        return 0;
    }

    //相交
    Vector v1, v2;
    d = Length(p1-p2);
    v1 = p1-C1.c, v2 = p2-C1.c; 
    double s11 = Area(Length(v1), Length(v2), Length(d)), s12 = C1.r*C1.r*Angle(v1, v2) / 2;
    v1 = p1-C2.c, v2 = p2-C2.c; 
    double s21 = Area(Length(v1), Length(v2), Length(d)), s22 = C2.r*C2.r*Angle(v1, v2) / 2;

    //讨论交点位置
    if(dcmp(C1.c.x-p1.x) * dcmp(C2.c.x-p1.x) <= 0)
        printf("%.3lf\n", s12+s22 - s11-s21);
    else {
        if(C1.r > C2.r) printf("%.3lf\n", C2.S() + s21 - s22 - s11 + s12);
        else printf("%.3lf\n", C1.S() + s11 - s12 - s21 + s22); //WA
    }

    return 0;
}

更为简单的方法:
10ms,256 kB

#include<cstdio>
#include<cmath>
using namespace std;

int main() {
    const double pi = acos(double(-1));
    double x1, y1, r1, x2, y2, r2;
    scanf("%lf%lf%lf%lf%lf%lf", &x1, &y1, &r1, &x2, &y2, &r2);
    double d = hypot(x1-x2, y1-y2); //三角形第三边计算函数,计算了圆心距。
    if(d >= r1 + r2) { printf("0.000"); return 0; } //相离 外切
    if(r1 >= r2 + d) { printf("%.3lf\n", pi*r2*r2); return 0; } //内含 内切
    double m = (r1*r1 - r2*r2 + d*d) / 2 / d; //m=r1*cosα,或者说r1在两圆连心线上的投影的长度。
    double n = sqrt(r1*r1-m*m); //交点到连心线的距离。
    double o = r1*r1*pi*(asin(n/r1)/pi) - n*m; //用角度计算弧形的面积。
    double p = r2*r2*pi*(asin(n/r2)/pi) - n*fabs(d-m);
    if(m > d) p = r2*r2*pi - p; //画图推导一下。
    printf("%.3lf\n", o + p);
    return 0;
}
posted @ 2015-02-07 23:09  wfwbz  阅读(170)  评论(0编辑  收藏  举报