mark一个宝藏文章,个人认为简单易懂:
添加链接描述
附上博主代码:
#include<cstdio>
#include<cmath>
const double PI=acos(-1.0);
const double eps=1e-5;//比较精度
//求圆心角之和
double totalCornerAngles(double edges[],int n,double r){
double sum = 0.0;
for(int i =0;i<n;i++)
sum+=asin(edges[i]/2/r)*2;
return sum;
}
int main(){
int N;//边数
scanf("%d",&N);//输入边数
double edges[100];//边长数组
double sum;//圆心角之和
double maxAngle=0.0;//最长边对应的圆心角
double maxEdge=0.0;//最长边
//初始化edges
for(int i=0;i<N;i++){
scanf("%lf",&edges[i]);
if(edges[i]>maxEdge)
maxEdge = edges[i];//保存最大边
}
//以最长边为直径求圆心角之和,若为2π则直接返回
sum = totalCornerAngles(edges,N,maxEdge/2);
if(fabs(sum-PI*2)<eps){
printf("外接圆的最大半径是最大边的一半:%.2f",maxEdge/2);
return 0 ;
}
//半径大于最大边的一半(即斜边大于直角边)
double left =maxEdge/2,right=10000000,mid;
double other=0;
//在误差范围内循环求解
while(right -left >eps){
mid = (right + left) /2;
maxAngle=asin(maxEdge/2/mid)*2;//求出最大边对应的圆心角
sum = totalCornerAngles(edges,N,mid);
other=sum-maxAngle;
//如果除去最大圆心角的其他圆心角之和小于π,说明圆心在多边形外面
if(other<PI){
sum=other+2*PI-maxAngle;
if( sum<2*PI)
left = mid;
else
right = mid;
}
//圆心在多边形里面
else{
if(sum>2*PI)
left = mid;
else
right = mid;
}
}
printf("外接圆的最大半径是:%.2lf",mid);
return 0;
}
补上自己的一点思考:
将边长从小到大排序。
先从边长考虑:
首先如果圆的周长小于边长和说明r过小。(避免构造圆上边出现首尾相连的情况,不是很精确,可以辅以圆心角之和<2PI)
在已假定半径为r的圆上任选一个起点,开始以每一条边长edge[i]为半径,假定为逆时针方向选点,知道选择到第n-1个点时:
落点为n1,2 ,如果此时点1与点n-1的距离恰好等于edge[n],则表示找到解,
否则若dist(1,n-1)>edge[n],则说明构造出来的圆太小了,mid<rmax,mid向右更新。
若dist(1,n-1)<edge[n],mid向左更新。
转换成角度考虑:若前N-1个圆心角之和>PI,则说明圆心在多边形内部,考虑SUM all > 2PI 圆小了,SUM all < 2PI 圆大了。
若前n-1个圆心角之和小于PI, 则说明圆心在多边形外部,若SUM n-1 > angle max 则圆大了, SUM n-1 < angle max 则圆小了。
结果均用二分维护到eps允许范围即可。