计算几何——空袭计算

 汇总篇:计算几何汇总 


 

题目描述

649.计算几何-空袭计算 (10分) 

C时间限制:3000 毫秒 |  C内存限制:3000 Kb


题目内容:


 轰炸机轰炸的目标区域是地面上一个n个顶点的多边形,现在三维坐标(x,y,h)处投一颗炸弹,飞行速度是(vx,vy,0),
重力加速度是10。炸弹的爆炸半径是r, 现需要了解被炸的区域面积.

输入描述


第一行是x y h
第二行是vx vy
第三行是炸弹的爆炸半径r
第四行是n,代表目标区域的顶点数
下面是n行是目标多变形的顶点坐标,按顺时针给出


输出描述


被炸的区域面积(两位小数)


输入样例


0 0 2000
100 0
100 
4
1900 100
2000 100
2000 -100
1900 -100


输出样例


15707.96

解题思路 

先根据物理学知识计算出落地圆心,然后就是多边形与圆相交面积的计算了

计算圆心

vxy=sqrt(vx*vx+vy*vy);
h = g*t*t/2;-> t = sqrt(2*h/g);
L = vxy*t;
S = (x,y)
e = (vx,vy)/dis((0,0),(vx,vy));
E = S + e*L;

多边形与圆相交面积请查看我另一篇博客,有详细介绍

ac代码

//c++ 提供默认的 = 赋值函数 和拷贝构造函数 
#include<iostream>
#include<cmath> 
#include<cstdio>
#include<vector>
using namespace std;
const int eps = 1e-2;
double myRound(double a){//因为小数有误差,所以判断相切时要精确到固定位数 
	return floor(a * 100 + 0.5) / 100; /*保留小数点后2位*/
}
class point{
 	public:
	double x;
	double y;
	point(double x_=0,double y_=0):x(x_),y(y_){} 
	friend const point operator+(const point& p1,const point& p2){
		return point(p1.x+p2.x,p1.y+p2.y);
	};
	friend const point operator-(const point& p1,const point& p2){
		return point(p1.x-p2.x,p1.y-p2.y);
	};
	friend const point operator*(const point& p,const double& m){
		return point(p.x*m,p.y*m);
	};
	friend const point operator*(const double& m,const point& p){
		return point(p.x*m,p.y*m);
	};
	friend const point operator/(const point& p,const double& m){
		return point(p.x/m,p.y/m);
	};
	friend ostream& operator <<(ostream& out,point& a){
		printf("(%lf,%lf)",a.x,a.y);
		return out;
	};
};
typedef point vect2;//重命名,向量也是用坐标表示 
 
class line{
	public:
	point start;
	point end; 
	line(point s=point(0,0),point e=point(0,0)):start(s),end(e){}
};
 
class circle{
	public:
		double r;
		point O;	
		circle(point O_=point(0,0),double r_=0):O(O_),r(r_){}
};
class polygon{//这里使用浅拷贝,更快 
	public:
		vector<point> p;
		int n;
		polygon(vector<point> q= vector<point>(),int n_=0){p=q;n=n_;}
};
 
//函数声明------------------------------------------------------------------
double dot(point O,point A,point B); 
double cross(point O,point A,point B);
double dis(const point &p1,const point &p2);
double angle(point O,point A,point B);
double sectorArea(point O,point A,point B,double r);
double disOfPointToLine(point O,line l);
point shadowPointOfPointToLine(point O,line l);
bool pointIsOnLine(point O,line L);
bool interPointOfLineAndCircle(line l,point O,double r,vector<point> &ans);
double InterAreaOfCircleAndPolygon(polygon poly,circle c);
//-------------------------------------------------------------------------- 
double dot(point O,point A,point B){//点乘 
	double oa_x=A.x-O.x;
	double oa_y=A.y-O.y;
	double ob_x=B.x-O.x;
	double ob_y=B.y-O.y;
	return oa_x*ob_x+oa_y*ob_y;
}
double cross(point O,point A,point B){//叉乘 
	double oa_x=A.x-O.x;
	double oa_y=A.y-O.y;
	double ob_x=B.x-O.x;
	double ob_y=B.y-O.y;
	return oa_x*ob_y-oa_y*ob_x;
}
double dis(const point &p1,const point &p2){//求两点之间距离
	double ans=(p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y);
	return sqrt(ans);
}
double dis2(const point &p1,const point &p2){//求两点之间距离 
	return(p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y);	 
}
//两直线夹角 
double angle(point O,point A,point B){	
	return acos(dot(O,A,B)/(dis(O,A)*dis(O,B)));
}
//扇形面积alpha*pi*r*r/2*pi
double sectorArea(point O,point A,point B,double r){
	double alpha=angle(O,A,B);
	return alpha*r*r/2;
}
//点到直线的距离 
double disOfPointToLine(point O,line l){
	double cos0=dot(l.start,O,l.end)/(dis(O,l.start)*dis(l.start,l.end));
	return dis(O,l.start)*sin(acos(cos0));		
} 
//点在直线上的投影  
point shadowPointOfPointToLine(point A,line l){//投影点出了问题 
	point B = l.start;
	point C = l.end;
	point D;
	vect2 BC = C - B;
	vect2 BA = B - A;
	vect2 BD = dot(B,A,C)/dis2(B,C)*BC; 
	D = B + BD;
	return D;	
} 
 
//点是否在线段上 
bool pointIsOnLine(point O,line L){
	point S=L.start;
	point E=L.end;
	if(cross(O,S,E)==0//在直线L上 
	and min(S.x,E.x)<=O.x&&O.x<=max(S.x,E.x)
	and min(S.y,E.y)<=O.y&&O.y<=max(S.y,E.y))
		return true;
	else return false; 
} 
//线段到圆的交点,所需函数:点到直线的距离,点在直线上的投影,两点之间的距离,判断点是否在线段上 
bool interPointOfLineAndCircle(line l,point O,double r,vector<point> &ans){
	ans.clear();
	point C,D,E;
	double d= disOfPointToLine(O,l);	
	E = shadowPointOfPointToLine(O,l);		
	vect2 AB = l.end-l.start;
	vect2 e=AB/dis(l.start,l.end);
	double base2 =myRound(r*r-d*d);//取约数,不然本来是0的会得不到0 
	//相切base^2 =0 两个点相等,相离 base^2<0 
	if(base2<0)return false;
	double base = sqrt(base2);	
	C = E - e*base;
	D = E + e*base;
	//判断C,D时候在线段AB上即可 
	if(pointIsOnLine(C,l))ans.push_back(C);
	if(pointIsOnLine(D,l)&&!(D.x==C.x&&D.y==C.y))//避免点重复 
		ans.push_back(D);
	if(ans.size()>0)return true;
	else return false;		
}
 
 
//多边形和圆相交的面积,所需函数:点到直线的距离,线段和圆的交点,扇形面积,两点之间的距离 
double InterAreaOfCircleAndPolygon(polygon poly,circle c){
	double sums=0;//有向面积 
	vector<point> pointsInCircle;
	vector<point> pointsOutCircle;
	vector<point> interPoints;
	for(int i=0;i<poly.n;i++){
		double s=0;	 
		pointsInCircle.clear();
		pointsOutCircle.clear();
		line l(poly.p[i],poly.p[(i+1)%poly.n]);//取多边形的一条边 
		double d = disOfPointToLine(c.O,l);//圆心到这条边的距离 
		//判断边的两个端点是在圆内还是圆外 
		if(dis(l.start,c.O)<=d){
			pointsInCircle.push_back(l.start);
		}else pointsOutCircle.push_back(l.start);//靠l的start方向的点先入vector 	
		if(dis(l.end,c.O)<=d){
			pointsInCircle.push_back(l.end);
		}else pointsOutCircle.push_back(l.end);
		
		//分四种情况 
		if(pointsInCircle.size()==2){//1:两个点都在圆内 
			//cout<<"三角形:"<<s<<endl; 
			s = cross(c.O,l.start,l.end)/2;
		}else if(pointsInCircle.size()==1){//2:一个点在圆内,一个点在圆外 
			interPoints.clear();
			//这里出错了没有求得交点 
			interPointOfLineAndCircle(l,c.O,c.r,interPoints);//只有一个交点 
			//定比分点求另一个交点,即圆外的顶点与圆心相连的线与圆的交点 
			vect2 e = (pointsOutCircle[0] - c.O)/dis(pointsOutCircle[0],c.O);
			point interPoint2 = c.O + e*c.r;
			double s1 = cross(c.O,pointsInCircle[0],interPoints[0]);
			double s2 = sectorArea(c.O,interPoints[0],interPoint2,c.r);
			//cout<<"三角形:"<<s1<<"扇形:"<<s2<<endl;
			s = s1 + s2;
		}else if(pointsInCircle.size()==0){//两个顶点都在圆外 
			if(d<=c.r){//3:两顶点所在边与圆相交 
			 	interPoints.clear();
				interPointOfLineAndCircle(l,c.O,c.r,interPoints);//这里有两个交点,在l的靠start方向的点先进入vector 
				//定比分点求另外两个交点,即圆外的顶点与圆心相连的线与圆的交点 
				vect2 e1 = (pointsOutCircle[0] - c.O)/dis(pointsOutCircle[0],c.O);
				vect2 e2 = (pointsOutCircle[1] - c.O)/dis(pointsOutCircle[1],c.O);
				interPoints.push_back(point(c.O + e1*c.r)); 
				interPoints.push_back(point(c.O + e2*c.r)); 
				double s1 = cross(c.O,interPoints[0],interPoints[1]);
				double s2 = sectorArea(c.O,interPoints[0],interPoints[2],c.r);
				double s3 = sectorArea(c.O,interPoints[1],interPoints[3],c.r);
				//printf("三角形:%lf 扇形:%lf %lf\n",s1,s2,s3);
				s = s1 + s2 +s3;	
			}else{//4:两顶点所在边与圆不相交
				interPoints.clear();
				vect2 e1 = (pointsOutCircle[0] - c.O)/dis(pointsOutCircle[0],c.O);
				vect2 e2 = (pointsOutCircle[1] - c.O)/dis(pointsOutCircle[1],c.O);
				interPoints.push_back(point(c.O + e1*c.r)); 
				interPoints.push_back(point(c.O + e2*c.r)); 				 
				s = sectorArea(c.O,interPoints[0],interPoints[1],c.r); 
				//cout<<"扇形:"<<s<<endl;
			}
		}
		sums+=s;
		//cout<<"s="<<s<<endl;		
	}
	return fabs(sums);		
}
 
/*
第一行是x y h
第二行是vx vy
第三行是炸弹的爆炸半径r
第四行是n,代表目标区域的顶点数
下面是n行是目标多变形的顶点坐标,按顺时针给出
vxy=sqrt(vx*vx+vy*vy);
h = g*t*t/2;-> t = sqrt(2*h/g);
L = vxy*t;
S = (x,y)
e = (vx,vy)/dis((0,0),(vx,vy));
E = S + e*L;
*/
double x,y,h,vx,vy,r,n;
const int N = 10005;
vector<point> P;
double px,py;
circle c;
polygon poly;
circle getCircle(){
	double vxy;
	double t;
	double L;
	double g=10.0;
	point S;
	vect2 e;
	vect2 Vxy;
	point E;
	vxy=sqrt(vx*vx+vy*vy);
	t = sqrt(2*h/g);
	L = vxy*t;
	S=point(x,y);
	Vxy = point(vx,vy);
	e = Vxy/dis(point(0,0),Vxy);
	E = S + e*L;
	//cout<<"circle:"<<E.x<<" "<<E.y<<" r="<<r<<endl;
	return circle(E,r);	
}
void solve(){
	c = getCircle(); 
	printf("%.2lf\n",InterAreaOfCircleAndPolygon(poly,c)+eps);
}
int main(){
while(~scanf("%lf%lf%lf",&x,&y,&h)){
	scanf("%lf%lf",&vx,&vy);
	scanf("%lf",&r);
	scanf("%lf",&n);
	P.clear();
	for(int i=0;i<n;i++){
		scanf("%lf%lf",&px,&py);
		P.push_back(point(px,py));
	}
	poly=polygon(P,n);
	solve();	
}	 
	return 0;	
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值