二维计算几何基础
参考资料
例题
给出一个没有缺口的简单多边形,它的边是垂直或者水平的,要求计算多边形的面积。Code (1)
#include <bits/stdc++.h>
using namespace std;
const int N=105;
struct Point
{
double x,y;
}a[N];
double Cross(Point A,Point B){return A.x*B.y-A.y*B.x;}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i].x>>a[i].y;
double ans=0;
for(int i=1;i<=n;i++)ans+=Cross(a[i],a[i%n+1]);
cout<<fabs(ans)/2<<'\n';
return 0;
}
从一个圆孔里看一个凸多边形,为了让看到的面积至少为 ,孔的半径至少需要多大?
假设孔的圆心固定在 ,且 在多边形的内部(而不是外部或边界上)。 随着圆的半径增大,能看到的面积单调不降,所以可以二分寻找满足条件的最小半径。 将多边形的每个顶点与原点连接,构成多个三角形。每个三角形都有一个对应圆心角的扇形。 通过分类讨论每个三角形与其对应扇形的交集面积,可以计算出能看到的面积。 :表示三角形 的有向面积。 通过向量叉积计算 : :表示由 和 两条边围成且半径为 的扇形的有向面积。 通过向量的叉积和点积计算出扇形的圆心角 : 根据扇形面积公式计算 : :表示每个三角形与其对应扇形的交集面积。 接下来通过分类讨论计算 ,设: 如果点 距离圆心的距离都小于 : 此时扇形完全包含三角形,面积交集就是三角形的面积: 接下来我们需要判断直线 是否与圆有交点 。 直线 的参数方程: 代入圆的方程: 整理为标准二次方程: 计算方程的系数: 计算判别式 : 如果直线与圆没有交点或相切: 此时三角形完全包含扇形,面积交集就是扇形的面积: 接下来通过求根公式,计算出 : 代入直线方程,计算出直线与圆的交点 : 我们令距离 较近的交点为 ,距离 较近的交点为 : 如果点 都在点 的同一侧(即线段 的延长线上): 此时三角形也完全包含扇形,面积交集就是扇形的面积: 如果点 在线段 上: 此时 是三角形面积, 是扇形面积: 如果点 在线段 上: 此时 是扇形面积, 是三角形面积: 否则点 都在线段 上。 此时 是扇形面积, 是三角形面积, 是扇形面积:Solution
参考资料
解题思路
函数定义
f1(A,B)
double f1(Point A,Point B)
{
return Cross(A,B)/2;
}f2(A,B,r)
double f2(Point A,Point B,double r)
{
return atan2(Cross(A,B),Dot(A,B))*r*r/2;
}calc(A,B,r)
分类讨论
Case 1
if(Len(A)<=r&&Len(B)<=r)return f1(A,B);判断相交
double a=Len2(B-A),b=Dot(A,B-A)*2,c=Len2(A)-r*r;double d=b*b-a*c*4;Case 2
if(d<=0)return f2(A,B,r);计算交点
double t1=(-b-sqrt(d))/(a*2),t2=(-b+sqrt(d))/(a*2);Point C=A+(B-A)*t1,D=A+(B-A)*t2;Case 3
if(t1>=1||t2<=0)return f2(A,B,r);Case 4
if(t1<=0)return f1(A,D)+f2(D,B,r);Case 5
if(t2>=1)return f2(A,C,r)+f1(C,B);Case 6
return f2(A,C,r)+f1(C,D)+f2(D,B,r);参考代码
#include <bits/stdc++.h>
using namespace std;
const double eps=1e-8;
const int N=55;
struct Point
{
double x,y;
Point(){}
Point(double _x,double _y):x(_x),y(_y){}
Point operator+(Point B){return Point(x+B.x,y+B.y);}
Point operator-(Point B){return Point(x-B.x,y-B.y);}
Point operator*(double k){return Point(x*k,y*k);}
}a[N];
double Dot(Point A,Point B){return A.x*B.x+A.y*B.y;}
double Cross(Point A,Point B){return A.x*B.y-A.y*B.x;}
double Len(Point A){return sqrt(Dot(A,A));}
double Len2(Point A){return Dot(A,A);}
double f1(Point A,Point B){return Cross(A,B)/2;}
double f2(Point A,Point B,double r){return atan2(Cross(A,B),Dot(A,B))*r*r/2;}
double calc(Point A,Point B,double r)
{
if(Len(A)<=r&&Len(B)<=r)return f1(A,B);
double a=Len2(B-A),b=Dot(A,B-A)*2,c=Len2(A)-r*r;
double d=b*b-a*c*4;
if(d<=0)return f2(A,B,r);
double t1=(-b-sqrt(d))/(a*2),t2=(-b+sqrt(d))/(a*2);
Point C=A+(B-A)*t1,D=A+(B-A)*t2;
if(t1>=1||t2<=0)return f2(A,B,r);
if(t1<=0)return f1(A,D)+f2(D,B,r);
if(t2>=1)return f2(A,C,r)+f1(C,B);
return f2(A,C,r)+f1(C,D)+f2(D,B,r);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
double s;
cin>>n>>s;
for(int i=1;i<=n;i++)cin>>a[i].x>>a[i].y;
double l=0,r=10000;
while(r-l>eps)
{
double mid=(l+r)/2,sum=0;
for(int i=1;i<=n;i++)sum+=calc(a[i],a[i%n+1],mid);
if(fabs(sum)>=s)r=mid;
else l=mid;
}
cout<<fixed<<setprecision(2)<<l<<'\n';
return 0;
}