题意:给定圆心+半径,以及矩形左下角和右上角坐标,求相交面积;
这道题说难不难,说简单也不简单;如果写过类似的话,那么就这道题就是签到题;
那么下面来解释一下如何用向量来解决这个几何问题:
首先我们来看看这个问题:
如果在知道凸多边形的顶点,求一个凸多边形的面积怎么求?
这里举例简单5边形:
那么我取一个O点在中间;
那么就有:
很明显如果知道每个点的坐标是不是根据向量叉乘的几何意义就可以求出面积了:
那么计算式子就是:
以上叉乘我规定向外面为正,那么这样算出来是不是就是面积了(因为矢量合成)?对吧;
明白了这个原理,其实我们O点取平面上任意一个点都可以这样计算,只要方向是对的那么对于凸多边形面积就OK了,我上面写的逆时针方向。
上面的知识点搞定后,我们来看下面这个加上了圆的,那么他的面积怎么算呢?
也就是怎么算这个面积呢?
我们把 圆心作为空间中的一点然后连接凸多边形的各个定点;
那么可以得到这个图:
然后再把各个交点写上:
那么是不是我们可以发现这个问题:
由△p3p4O构成的三角形和圆的相交面积+△p4p0O构成的三角形和圆的相交面积+△p0p1O构成的三角形和圆的相交面积-CDOp2的面积-ABp2O的面积是不是就是相交的面积了?
所以求相交面积就可以有思路了;
我们只需要把每个三角形与原的相交面积算出来是不是就可以得出相交面积了?
好了这里就很nice了,知道思路后我们只需要从向量+几何角度入手就可以搞定这道题了;
那么如何算这个三角形与圆的相交面积呢?
那么接下来就分几种情况去讨论了;
1.如果两个点都在圆外面:
(1)
可以发现b角,c角是锐角
(2)
可以发现b角,c角是锐角
(3)
可以发现b角是钝角,c角是锐角
2.如果两个点都在圆内:
可以发现在圆内,那么面积就直接等于两个向量的叉乘,也就是:
上面两个向量叉乘的模1/2;
3.一个点在圆外一个点在圆内:
(1)
可以发现b,c角都是锐角
(2)
可以发现b角是钝角,c角是锐角;
好了所有情况已经就位了;
那么我们对所有情况补一个高h,是不是知道了三角形面积和线段bc的长度,我们就可以求面积了?
我拿其中一个来举例计算一下面积,其他的计算方法类似:
所以上面的扇形面积就可以求出来了s=1/2rrseta;
所以由于对称关系所以就可以算出这个三角形和圆的相交面积了;
其余剩下的只需要做辅助线+找角度关系都可搞定;
那么面积问题解决后那么就是算出面积的值的正负问题了;
其实正负问题很好搞定;
通过顺时针向量的叉乘就可以搞定了;
什么意思呢?
就是这个图
我们规定方向朝外为正,那么如果按照顺时针叉乘出来的是负的,那么这个面积肯定就是下面这个图的蓝色部分:
所以这个问题就很好的解决了;
以后长个经验,如果遇见几何问题都考虑向量+角度关系解决问题了;
AC代码:
#include<bits/stdc++.h>
#define inf 1000000000000
#define M 100009
#define eps 1e-12
#define PI acos(-1.0)
using namespace std;
struct Point
{
double x,y;
Point(){
}
Point(double xx,double yy){
x=xx;y=yy;}
Point operator -(Point s){
return Point(x-s.x,y-s.y);}
Point operator +(Point s){
return Point(x+s.x,y+s.y);}
double operator *(Point s){
return x*s.x+y*s.y;}
double operator ^(Point s){
return x*s.y-y*s.x;}
}p[M];
double max(double a,double b){
return a>b?a:b;}
double min(double a,double b){
return a<b?a:b;}
double len(Point a){
return sqrt(a*a);}
double dis(Point a,Point b){
return len(b-a);}//两点之间的距离
double cross(Point a,Point b,Point c)//叉乘
{
return (b-a)^(c-a);
}
double dot(Point a,Point b,Point c)//点乘
{
return (b-a)*(c-a);
}
int judge(Point a,Point b,Point c)//判断c是否在ab线段上(前提是c在直线ab上)
{
if (c.x>=min(a.x,b.x)
&&c.x<=max(a.x,b.x)
&&c.y>=min(a.y,b.y)
&&c.y<=max(a.y,b.y)) return 1;
return 0;
}
double area(Point b,Point c,double r)
{
Point a(0.0,0.0);
if(dis(b,c)<eps) return 0.0;
double h=fabs(cross(a,b,c))/dis(b,c);
if(dis(a,b)>r-eps&&dis(a,c)>r-eps)//两个端点都在圆的外面则分为两种情况
{
double angle=acos(dot(a,b,c)/dis(a,b)/dis(a,c));//这个就是上图的seta
if(h>r-eps) return 0.5*r*r*angle;//这个就是1(1)的情况
else if(dot(b,a,c)>0&&dot(c,a,b)>0)//如果都是锐角 这就是算的面积
{
double angle1=2*acos(h/r);
return 0.5*r*r*fabs(angle-angle1)+0.5*r*r*sin(angle1);
}else return 0.5*r*r*angle;
}else
if(dis(a,b)<r+eps&&dis(a,c)<r+eps) return 0.5*fabs(cross(a,b,c));//两个端点都在圆内的情况
else//一个端点在圆上一个端点在圆内的情况
{
if(dis(a,b)>dis(a,c)) swap(b,c);//默认b在圆内
if(fabs(dis(a,b))<eps) return 0.0;//ab距离为0直接返回0
if(dot(b,a,c)<eps)
{
double angle1=acos(h/dis(a,b));
double angle2=acos(h/r)-angle1;
double angle3=acos(h/dis(a,c))-acos(h/r);
return 0.5*dis(a,b)*r*sin(angle2)+0.5*r*r*angle3;
}else
{
double angle1=acos(h/dis(a,b));
double angle2=acos(h/r);
double angle3=acos(h/dis(a,c))-angle2;
return 0.5*r*dis(a,b)*sin(angle1+angle2)+0.5*r*r*angle3;
}
}
}
int main()
{
double x,y,R;//圆心+半径
double Ax,Ay,Bx,By;
scanf("%lf%lf%lf%lf%lf%lf%lf",&x,&y,&R,&Ax,&Ay,&Bx,&By);
p[1].x=Ax;p[1].y=Ay;//初始化凸多边形顶点时按照顺时针方形或者逆时针,我这里是按照顺时针
p[2].x=Ax;p[2].y=By;
p[3].x=Bx;p[3].y=By;
p[4].x=Bx;p[4].y=Ay;
p[5]=p[1];//需要多存一个起始点
Point O(x,y);// 初始化圆心坐标
for(int i=1;i<=5;i++) p[i]=p[i]-O;//向量
O=Point(0,0);//把圆心转化为了原点,这点很重要,这点如何理解?就是向量的几何意义和代数的转化
double sum=0;
for(int i=1;i<=4;i++)
{
int j=i+1;
double s=area(p[i],p[j],R);
if(cross(O,p[i],p[j])>0) sum+=s;//这里就是判断方向面积的正负
else sum-=s;
}
printf("%.4lf\n",fabs(sum));//最后输出绝对值
return 0;
}