多邊形與圓相交面積
阿新 • • 發佈:2018-12-11
HDU2892求多邊形與圓相交面積
Problem Description
小白最近被空軍特招為飛行員,參與一項實戰演習。演習的內容是轟炸某個島嶼。。。
作為一名優秀的飛行員,任務是必須要完成的,當然,憑藉小白出色的操作,順利地將炸彈投到了島上某個位置,可是長官更關心的是,小白投擲的炸彈到底摧毀了島上多大的區域?
島是一個不規則的多邊形,而炸彈的爆炸半徑為R。
小白只知道自己在(x,y,h)的空間座標處以(x1,y1,0)的速度水平飛行時投下的炸彈,請你計算出小白所摧毀的島嶼的面積有多大. 重力加速度G = 10.
Input
首先輸入三個數代表小白投彈的座標(x,y,h);
然後輸入兩個數代表飛機當前的速度(x1, y1);
接著輸入炸彈的爆炸半徑R;
再輸入一個數n,代表島嶼由n個點組成;
最後輸入n行,每行輸入一個(x’,y’)座標,代表島嶼的頂點(按順勢針或者逆時針給出)。(3<= n < 100000)
Output
輸出一個兩位小數,表示實際轟炸到的島嶼的面積。
先根據簡單的初中物理牛頓力學的知識求出圓心位置。。
以圓心和多邊形兩個相鄰的頂點構成三角形,一共有n個這樣的三角形。
求每個三角形與圓相交的面積,累加求和。
分三種情況:
case1: 兩點都在圓內,相交面積即為三角形面積
case2: 一個點在圓內,一個在外部,相交面積為小三角形面積+扇形面積
case3:兩點在圓外,考慮線段與圓交點數:
小於2時,相交面積為扇形面積;
等於2,相交面積為大扇形面積-小扇形面積+小三角形面積
利用叉積可以求出三角形面積
扇形面積公式為 angR
#include <bits/stdc++.h> using namespace std; const double eps = 1e-8; int dcmp(double x){if (fabs(x)<eps) return 0;return x<0?-1:1;}//精度比較 struct Point{ //點結構體 double x,y; // 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 -(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); } 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));} double Angle(Vector A,Vector B) { return acos(Dot(A,B)/Length(A)/Length(B));} bool OnSegment(Point p, Point a1, Point a2){ return dcmp(Cross(a1-p,a2-p))==0 && dcmp(Dot(a1-p,a2-p))<=0; } struct Line{ //直線結構體 Point p; //點 Vector v; //向量判方向即速度 Line(){} //空構造器 Line(Point p,Vector v):p(p),v(v){}//建構函式 Point point(double t){ //傳入時間 return p+v*t; //從P點往V方向走T時間後的點 } }; struct Circle{ //圓結構體 Point c; //點 double r; //半徑 Circle(){} //空構造器 Circle(Point c, double r):c(c),r(r){}//圓 }; int getLineCircleIntersection(Line L,Circle C, 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; //無解,則無交點 double t1,t2; //宣告兩個解 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;//有兩個解,兩個交點 } double get_Circle_polygon_Intersect_area(Point A, Point B,Circle C){//傳入線段與圓 Vector CA = C.c-A, CB = C.c-B; //圓心與線段兩點向量 double da = Length(CA), db = Length(CB);//圓心與兩點的距離 da = dcmp(da-C.r); //圓心到兩點的距離與當前圓的半徑之差 db = dcmp(db-C.r); //圓心到兩點的距離與當前圓的半徑之差 if (da<=0 && db<=0)return fabs(Cross(CA,CB))*0.5;//如果半徑大於等於到兩點距,直接就是三角形面積 vector<Point>sol; //開一個動態陣列存點 int num = getLineCircleIntersection(Line(A,B-A),C,sol);//求出線圓的交點並存到SOL裡面 double cnt = C.r*C.r; //得到半徑的平方 Point q; if (da<=0 && db>0) {//如果A點在圓內,B點在圓外,相交面積為小三角形面積+扇形面積 q = OnSegment(sol[0],A,B)?sol[0]:sol[1]; double area = fabs(Cross(CA,C.c-q))*0.5; double ang = Angle(CB,C.c-q); return area+cnt*ang*0.5; } if (db<=0 && da>0) {//如果B點在圓內,A點在圓外,相交面積為小三角形面積+扇形面積 q = OnSegment(sol[0],A,B)?sol[0]:sol[1]; double area = fabs(Cross(CB,C.c-q))*0.5; double ang = Angle(CA,C.c-q); return area+cnt*ang*0.5; } if (num==2) {//第四種情況,就是兩點都在圓外且有圓與線段有兩個交點,相交面積為大扇形面積-小扇形面積+小三角形面積 double big_area = cnt*Angle(CA,CB)*0.5; double small_area = cnt*Angle(C.c-sol[0],C.c-sol[1])*0.5; double delta_area = fabs(Cross(C.c-sol[0],C.c-sol[1]))*0.5; return big_area+delta_area-small_area; } return cnt*Angle(CA,CB)*0.5;//第五種情況就是兩點都在圓外且有圓與線段的交點個數小於2,相交面積為扇形面積; } Circle bomb; double X1,Y1,R,X0,Y0,h,x,y; const int maxn = 100000+5; Point p[maxn]; int n; int main(){ while (scanf("%lf%lf%lf",&X0,&Y0,&h)==3){//輸入三個數代表小白投彈的座標(x,y,h) scanf("%lf%lf",&X1,&Y1); //輸入兩個數代表飛機當前的速度(x1, y1); scanf("%lf",&R); //輸入炸彈的爆炸半徑R; double t = sqrt(0.2*h); //得到時間,因為二分之一GT方等於H,G取10 bomb = Circle(Point(X0+X1*t,Y0+Y1*t),R);//炸彈炸出的圓 scanf("%d",&n); //輸入一個數n,代表島嶼由n個點組成; for (int i=0; i<n; i++){ //遍歷全部點 scanf("%lf%lf",&x,&y); //每行輸入一個(x',y')座標,代表島嶼的頂點 p[i] = Point(x,y); //把點打進數組裡面 } p[n] = p[0]; //第零個點也是第N個個點,因為要連邊 double area = 0; //初始化答案是0 for (int i=0; i<n; i++) { //逐條邊掃 double tmp = get_Circle_polygon_Intersect_area(p[i],p[i+1],bomb);//得相交區域面積 if (Cross(p[i]-bomb.c,p[i+1]-bomb.c)<0) tmp = -tmp;//利用差乘保持,圓心乘點心的向量大於零不變,小於零取反 area += tmp; //答案累加 } if (area<0) area = -area; //因為是用叉乘算面積且順逆方向不定故取正 printf("%.2f\n",area); //輸出 } return 0; } /* Sample Input 0 0 2000 100 0 100 4 1900 100 2000 100 2000 -100 1900 -100 Sample Output 15707.96 */