二分法求凸多邊形的外接圓最大半徑


這是一道來自《算法筆記》的題目

題目描述

給定 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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM