zoukankan      html  css  js  c++  java
  • 二分法求凸多边形的外接圆最大半径

    这是一道来自《算法笔记》的题目

    题目描述

    给定 N 个线段的长度,试将它们头尾相接(顺序任意)地组合成一个凸多边形,使得凸多边形的外接圆的半径最大,求该最大半径。其中 N 不超过 105 ,线段长度均不超过 100 ,要求算法中不涉及坐标的计算。


    考查内容

    二分算法的本质就是通过不断迭代使left 和 right 在固定条件下逐渐靠近真实值,符合一定误差,所以实际上把该题放在二分扩展里面,这个所谓的最大半径的“最大”是不在求解中的,最大应该算题干,先组成一个有外接圆的凸多边形,然后求其半径即可。不要误入歧途在“最大”上绞尽脑汁。


    分析

    • 首先半径一定大于等于最长线段的一半。
    • 这题的意思是这些线段组成的凸多边形的顶点一定能放在某个圆上,我们只需要考虑半径最大就行,不用考虑如何组成的这种图形。
    • 外接圆圆心与每个线段顶点连接后会有一个圆心角
    1. 如果圆心在凸多边形的内部,假设最大半径是rmax,则所有圆心角之和应该为2π。
      • 如果半径比rmax大,那么这些线段的圆心角之和sum<2∗π,线段没法首尾相连的在外接圆上,半径应变小
      • 如果半径比rmax小,那么这些线段的圆心角之和sum>2∗π,这时构成的外接圆不是半径最大的外接圆,半径应变大

     sum为绿色之和

          因此可以对半径长度二分,寻找圆心角之和为2∗π2*pi2∗π的那个值即可。

    2. 如果圆心在凸多边形的外部,假设最大半径是rmax,那么最长的线段的圆心角,一定等于其他线段的圆心角之和。取计算目标为+2π,此时情况取反。


     

    代码(引用https://blog.csdn.net/flashmsn/article/details/94642687)

    #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;
     }

    参考:https://blog.csdn.net/liuerin/article/details/98961797

     

  • 相关阅读:
    计蒜客模拟赛D2T2 蒜头君的排序:区间逆序对(移动端点) + 树状数组
    计蒜客模拟赛D2T1 蒜头君的兔子:矩阵快速幂
    计蒜客模拟赛D1T2 蒜头君的树:树上节点之间最短距离和
    计蒜客模拟赛D1T1 蒜头君打地鼠:矩阵旋转+二维前缀和
    Cubieboard安装系统
    awk速查手册
    sed速查手册
    常用正则表达
    MySQL索引小记
    jQuery中attr和prop的区别
  • 原文地址:https://www.cnblogs.com/transmigration-zhou/p/12275464.html
Copyright © 2011-2022 走看看