AcWing 3034. 望远镜 POJ3675 三角剖分求圆与多边形的面积交并

题意

https://www.acwing.com/problem/content/description/3037/

求以原点为圆心的圆与一个简单多边形的面积交

思路

将多边形每条边与圆心相连求出其围成的有向三角形面积(三角剖分思想) 相加即为答案

求次面积交要考虑的状态如下:(设多边形某条边上的两端点为Point a,b,如果线段ab与圆有交点 设交点为pa pb)

1.a b均在圆内 S = Cross(a,b)/2;

2.a b均在圆外 

①ab与圆心O的距离 >= R, S = aOb的扇形面积

②距离 < R,S = paOPb的扇形面积 + aOpa的有向S△ + pbOb的有向S△

3.a在圆内 b在圆外 S = bOpb的扇形 + aOpb的有向S△

4.b在圆内 a在圆外 S = paOa的扇形 + paOb的有向S△

以上,在通过扇形面积公式 线段与圆心间的关系处理细节即可

求出交就能就并~

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>

using namespace std;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;

int n;
double R;
struct Point{
	double x,y;
	Point(double x = 0,double y = 0) : x(x),y(y) {}
}p[55],O;
typedef Point Vector;

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

//运算符重载
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 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 dis(Point A,Point B){
	return sqrt((A.x-B.x)*(A.x-B.x) + (A.y-B.y)*(A.y-B.y));
}

double Length(Vector A){return sqrt(Dot(A,A));}//计算向量的长度

Point rotate(Point a, double b){
    return {a.x * cos(b) + a.y * sin(b), -a.x * sin(b) + a.y * cos(b)};
}

Vector norm(Vector a){//将向量a标准化
	return a / Length(a);
}

//P点再P1P2上 叉积==0
bool OnSeg(Point P,Point P1,Point P2){
	return dcmp(Cross(P1 - P,P2 - P)) == 0 && dcmp(Dot(P1 - P,P2 - P)) <= 0;
}

//判断共线
bool Collinear(Point P1,Point P2,Point P3,Point P4){
	double A = Cross(P1 - P2,P3 - P2);
	double B = Cross(P1 - P2,P4 - P2);
	if(!dcmp(A) && !dcmp(B)) return 1;
	return 0;
}

//两参数方程求交点,(点,向量,点,向量)
Point Getlinenode(Point P,Point v,Point Q,Point w){
	//线1(P1,P2) 线2(P3,P4) (P1,P1-P2,P3,P3-P4)
    Point u = P - Q;
    double t = Cross(w,u) / Cross(v,w);
    return P + v * t;
}

double get_sector(Point a,Point b){//Oa Ob扇形面积
	double angle = acos(Dot(a,b) / Length(a) / Length(b));//aOb夹角
	if(dcmp(Cross(a,b)) < 0) angle *= -1.0;//面积是有向的
	return R * R * angle / 2.0;//面积
}

//求圆与直线的交点
double get_C_L_Ist(Point a,Point b,Point &pa,Point &pb){//ab 与 O 相交于 papb
	Point e = Getlinenode(a,b - a,O,rotate(b - a,pi / 2.0));//e是O垂直于ab的垂足
	double mind = dis(O,e);//垂线的距离
	if(!OnSeg(e,a,b)){
		mind = min(dis(O,a),dis(O,b));//O到线段ab的最短距离
	}
	if(dcmp(R - mind) <= 0) return mind;//ab均在圆外 返回最短距离 不比再求交点
	double len = sqrt(R * R - dis(O,e)*dis(O,e));//欲加在e上的向量长度 勾股
	pa = e + norm(a - b) * len;
	pb = e + norm(b - a) * len;
	return mind;
}

double get_circle_triangle_area(Point a,Point b){
	double disa = dis(a,O),disb = dis(b,O);
	if(dcmp(R - disa) >= 0 && dcmp(R - disb) >= 0) return Cross(a,b)/2.0;//a b在圆内 等于S△
	if(Collinear(a,O,b,O)) return 0.0;//O a b三点共线
	Point pa,pb;//ab与圆的两个交点
	double mind = get_C_L_Ist(a,b,pa,pb);//线段ab与O的最近距离
	if(dcmp(R - mind) <= 0) return get_sector(a,b);//扇形aOb
	if(dcmp(R - disa) >= 0) return get_sector(pb,b)+Cross(a,pb)/2.0;
	if(dcmp(R - disb) >= 0) return get_sector(a,pa)+Cross(pa,b)/2.0;
	//扇形+△ 注意有向!
	return get_sector(a,pa)+get_sector(pb,b)+Cross(pa,pb)/2.0;//俩扇形+△
}

double work(){
	double ans = 0.0;
	for(int i = 0;i < n; i++){
		ans += get_circle_triangle_area(p[i],p[(i + 1) % n]);
	}
	return fabs(ans);
}

int main()
{
	O.x = 0,O.y = 0;
	while(~scanf("%lf %d",&R,&n)){
		for(int i = 0;i < n; i++) scanf("%lf %lf",&p[i].x,&p[i].y);
		printf("%.2f\n",work());
	}
}

 

上一篇:Educational Codeforces Round 102 (Rated for Div. 2)E题(分层图、最短路)


下一篇:[CF1479B2/CF1480D2] Painting the Array II