UVA 11978 Fukushima Nuclear Blast(二分,圆和简单多边形求公共面积)

题意:给一个简单多边形,点数不超过5000,再给一个圆心 和 整数P,要求确定圆的半径 r,使得s = 简单多边形面积,s1 = 圆与简单多边形的公共面积,且 s/s1 = P/100. r只要求精确到整数即可。
链接:
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=18797 (VJ)
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3129 (UVA)

解法:二分半径,直到找到合适的 r 为止。现在问题变为 如何求 圆和简单多边形的公共面积。这里的简单多边形,题目给了一个很好的定义,如下。( In geometry, a simple polygon is a closed polygonal chain of line segments in the plane which do not have points in common other than the common vertices of pairs of consecutive segments.) 简单多边形可以是凸多边形,也可以是凹多变形

我们这样做,将多边形每条边的两点和圆心连线,这样就得到若干个“中心三角形“,中心三角形与圆的公共面积分4种情况讨论。具体情况这篇博客:http://www.cnblogs.com/lxglbk/archive/2012/08/12/2634192.html 。个人认为这位博主已经讲述地十分到位了。

本题代码

//Hello. I'm Peter.
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
#define peter cout<<"i am peter"<<endl
typedef long long ll;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const double eps = 1e-9, pi = acos(-1.0);
inline int sgn(double x){
    if(fabs(x) < eps) return 0;
    else return x > 0? 1 : -1;
}
double sq(double x){return x*x;}

struct Point{
    double x, y;
    Point(){};
    Point(double x1, double y1){x = x1, y = y1;}
};
typedef Point Vector;
bool cmpxy(const Point a, const Point b){if(sgn(a.x-b.x)) return a.x < b.x; else return a.y < b.y;}
bool cmpyx(const Point a, const Point b){if(sgn(a.y-b.y)) return a.y < b.y; else return a.x < b.x;}
bool cmpYx(const Point a, const Point b){if(sgn(a.y-b.y)) return a.y > b.y; else return a.x < b.x;}
bool cmpyX(const Point a, const Point b){if(sgn(a.y-b.y)) return a.y < b.y; else return a.x > b.x;}
Vector operator + (const Vector a, const Vector b){return Vector(a.x + b.x, a.y + b.y);}
Vector operator - (const Vector a, const Vector b){return Vector(a.x - b.x, a.y - b.y);}
double operator * (const Vector a, const Vector b){return a.x * b.x + a.y * b.y;}
double operator % (const Vector a, const Vector b){return a.x * b.y - a.y * b.x;}
Vector operator * (const Vector a, const double b){return Vector(a.x * b, a.y * b);}
Vector operator * (const double b, const Vector a){return Vector(a.x * b, a.y * b);}
Vector operator / (const Vector a, const double b){return Vector(a.x / b, a.y / b);}
bool operator == (const Point a, const Point b){return sgn(a.x - b.x)==0 && sgn(a.y - b.y)==0;}
bool operator || (const Vector a, const Vector b){return sgn(a % b)==0;}
bool operator / (const Vector a, const Vector b){return sgn(a % b)!=0;}
double Length(Vector v){return (double)sqrt((double)(v.x * v.x + v.y * v.y));}
double LenSq(Vector v){return v.x*v.x + v.y*v.y;}
double Dis(Point a, Point b){return Length(a - b);}
Vector Rotate(Vector v, double rad){return Vector(v.x * cos(rad) - v.y * sin(rad), v.x * sin(rad) + v.y * cos(rad));}
Vector Norv(Vector v){return Vector(v.y, -v.x);}
Vector Unitv(Vector v){return v / Length(v);}
double angle(Vector v){return atan2(v.y, v.x);}
double angle(Vector a, Vector b){
    double ans = angle(a) - angle(b);
    while(sgn(ans) < 0) ans += 2*pi; while(sgn(ans) >= 2*pi) ans -= 2*pi;
    return fmin(ans, 2*pi - ans);
}
double Area_Tri(Point p1, Point p2, Point p3){return 0.5 * fabs((p2 - p1) % (p3 - p1));}
double Area_Tri(double a, double b, double c){
    double p = (a+b+c)/2;
    return (double)sqrt((double)(p*(p-a)*(p-b)*(p-c)));
}
double Area_Poly(Point *p, int n){
    //求任意简单多边形的无向面积,前提要将p逆时针或顺时针排序

    if(n <= 2) return 0;
    Point o = Point(0, 0);
    p[n++] = p[0];
    double ans = 0;
    for(int i = 0; i < n-1; i++){
        ans += Area_Tri(o, p[i], p[i+1]) * sgn((p[i] - o) % (p[i+1] - o));
    }
    return fabs(ans);
}


/*--------------------------直线--------------------------*/
struct Line{
    Point p; Vector v;
    Line(){};
    Line(Point p1, Vector v1){p = p1, v = v1;}
};
Point operator / (const Line a, const Line b){
    double t = ((b.p - a.p) % b.v) / (a.v % b.v);
    return a.p + a.v * t;
}
double Dis(Point p, Line l){return fabs(l.v % (p - l.p)) / Length(l.v);}
double angle(Line a, Line b){double ans = angle(a.v, b.v); return fmin(ans, pi - ans);}
double angle(Line l){
    double a = angle(Vector(1, 0), l.v);
    if(Rotate(Vector(1, 0), a) / l.v) a = pi - a;
    return a;
}


/*--------------------------线段--------------------------*/
struct Seg{
    Point p1, p2;
    Seg(){};
    Seg(Point p11, Point p22){p1 = p11, p2 = p22;}
};
bool PointOnSeg(Point p, Seg s){
    if((s.p1 - p) / (s.p2 - p)) return false;
    else if(sgn((s.p1 - p) * (s.p2 - p)) > 0) return false;
    else return true;
}
bool operator / (const Seg a, const Seg b){//此函数容易写错,且不同题目需要修改,特别注意
    if((a.p2 - a.p1) || (b.p2 - b.p1)){
        return PointOnSeg(a.p1, b) || PointOnSeg(a.p2, b) ||
        PointOnSeg(b.p1, a) || PointOnSeg(b.p2, a);
    }
    else return sgn((a.p2 - a.p1) % (b.p1 - a.p1)) * sgn((a.p2 - a.p1) % (b.p2 - a.p1)) <= 0 &&
        sgn((b.p2 - b.p1) % (a.p1 - b.p1)) * sgn((b.p2 - b.p1) % (a.p2 - b.p1)) <= 0 ;
}
bool operator / (const Line a, const Seg b){
    return sgn(a.v % (b.p1 - a.p)) * sgn(a.v % (b.p2 - a.p)) <= 0;
}

/*--------------------------圆--------------------------*/
struct Circle{
    Point p;
    double r;
    Circle(){};
    Circle(Point p1, double r1){p = p1, r = r1;}
    Point point(double rad){
        return Point(p.x + r * cos(rad), p.y + r * sin(rad));
    }
};
bool PointOnCircle(Point p, Circle c){return sgn(Dis(p, c.p) - c.r) == 0;}
bool PointInCircle(Point p, Circle c){return sgn(Dis(p, c.p) - c.r) < 0;}
bool PointOnInCircle(Point p, Circle c){return sgn(Dis(p, c.p) - c.r) <= 0;}
double Area_fan(Vector v1, Vector v2, double r){return 0.5 * angle(v1, v2) * r * r;}
double Area_fan(double rad, double r){return 0.5 * rad * r * r;}
void Poi_LineInterCircle(Line l, Circle c, Point *x, int &nx){
    Vector v1 = l.p - c.p;
    double A = LenSq(l.v), B = 2 * l.v * v1, C = LenSq(v1) - sq(c.r);
    double delta = B*B - 4*A*C;

    nx = 0;
    if(delta < 0) return;
    double t1, t2;
    t1 = (-B + (double)sqrt((double)delta)) / (2*A);
    t2 = (-B - (double)sqrt((double)delta)) / (2*A);
    x[nx++] = l.p + l.v * t1;
    if(delta > 0) x[nx++] = l.p + l.v * t2;
}
void Poi_SegInterCircle(Seg s, Circle c, Point *x, int &nx){
    Point xt[2];
    int nxt;
    Poi_LineInterCircle(Line(s.p1, s.p2 - s.p1), c, xt, nxt);

    nx = 0;
    for(int i = 0; i < nxt; i++){
        if(PointOnSeg(xt[i], s)) x[nx++] = xt[i];
    }
}
double ComArea_Circle_CenterTri(Circle c, Point p1, Point p2){
    //圆和中心三角形的公共面积; c和(c.p, p1, p2)的公共面积
    bool oi1 = PointOnInCircle(p1, c), oi2 = PointOnInCircle(p2, c);
    if(oi1 && oi2) return Area_Tri(p1, p2, c.p);
    else if(oi1 + oi2 == 1){
        if(oi1) swap(p1, p2);
        Point x[2]; int nx;
        Poi_SegInterCircle(Seg(p1, p2), c, x, nx);
        return Area_Tri(x[0], p2, c.p) + Area_fan(x[0] - c.p, p1 - c.p, c.r);
    }
    else{
        Point x[2]; int nx;
        Poi_SegInterCircle(Seg(p1, p2), c, x, nx);
        if(nx == 2){//注意这里的判断,不能判断圆心到线段所在直线的距离,那样是错的
            if(Dis(x[1], p1) < Dis(x[0], p1)) swap(x[0], x[1]);
            return Area_fan(p1 - c.p, x[0] - c.p, c.r) + Area_Tri(x[0], x[1], c.p) + Area_fan(p2 - c.p, x[1] - c.p, c.r);
        }
        else return Area_fan(p1 - c.p, p2 - c.p, c.r);
    }
}
double ComArea_Circle_Poly(Circle c, Point *p, int np){
    //圆和简单多边形的公共面积,前提要保证p逆时针或顺时针排序
    if(np <= 2) return 0.0;
    double ans = 0.0;
    p[np++] = p[0];
    for(int i = 0; i < np - 1; i++){
        if(p[i] == p[i+1]) continue;
        ans += ComArea_Circle_CenterTri(c, p[i], p[i+1]) * sgn((p[i] - c.p) % (p[i+1] - c.p));
    }
    return fabs(ans);
}

Point readPoi(){
    int x = read(), y = read();
    return Point(x, y);
}

#define N 10000
int n, P;
Point p[N];
Circle cir;

double erfen(){
    double l = 0, r = 10000, mid;
    double s = Area_Poly(p, n);
    while(r - l > 1e-4){
        mid = (l + r) * 0.5;
        cir.r = mid;
        double coms = ComArea_Circle_Poly(cir, p, n);
        if(sgn(coms * 100 - P * s) < 0) l = mid;
        else r = mid;
    }
    return l;
}

int main(){
    int T = read();
    for(int kase = 1; kase <= T; kase++){
        n = read();
        for(int i = 0; i < n; i++) p[i] = readPoi();
        cir.p = readPoi();
        P = read();

        double ans = erfen();
        printf("Case %d: %.0f\n",kase,ans);
    }
    return 0;
}

你可能感兴趣的:(二分,圆和简单多边形)