圓排列問題


問題

圓排列問題:給定n個圓的半徑序列,將它們放到矩形框中,各圓與矩形底邊相切,

求具有最小排列長度的圓排列。

解析

圓排列問題的解空間是一棵排列樹。按照回溯法搜索排列樹的算法框架,設開始時a=[r1,r2,……rn]是所給的n個元的半徑,則相應的排列樹由a[1:n]的所有排列構成。

1. center計算圓在當前圓排列中的橫坐標,由x^2 = sqrt((r1+r2)^2-(r1-r2)^2)推導出x = 2*sqrt(r1*r2)。

2.Compute計算當前圓排列的長度。變量lenmin記錄當前最小圓排列長度。數組r存儲所有圓的半徑。數組x則記錄當前圓排列中各圓的圓心橫坐標。

3. 在遞歸算法Backtrack中,當i>n時,算法搜索至葉節點,得到新的圓排列方案。此時算法調用Compute計算當前圓排列的長度,適時更新當前最優值。當i<n時,當前擴展節點位於排列樹的i-1層。此時算法選擇下一個要排列的圓,並計算相應的下界函數。

代碼

#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
 
const int N=4;
double minlen=10000,x[N],r[N];//分別為最小圓排列長度,每個圓心橫坐標,每個圓半徑
double bestr[N];//最小圓排列的半徑順序
 
double center(int t)//得到每個圓的圓心坐標
{
    double temp=0;
    for(int j=1;j<t;++j)//因為目標圓有可能與排在它之前的任一圓相切,故需一一判斷
    {
        double xvalue=x[j]+2.0*sqrt(r[t]*r[j]);// 
        if(xvalue>temp)
            temp=xvalue;
    }
    return temp;
}
void compute()
{
    double low=0,high=0;
    for(int i=1;i<N;++i)
    {
        if(x[i]-r[i]<low)
            low=x[i]-r[i];
        if(x[i]+r[i]>high)
            high=x[i]+r[i];
    }
    if(high-low<minlen)
    {
        minlen=high-low;
        for(int i=1;i<N;++i)
            bestr[i]=r[i];
    }
}
void backtrack(int t)
{
    if(t==N)
    {
        compute();
    }
    else
    {
        for(int j=t;j<N;++j)
        {
            swap(r[t],r[j]);
            double centerx=center(t);
            if(centerx+r[t]+r[1]<minlen)
            {
                x[t]=centerx;
                backtrack(t+1);
            }
            swap(r[t],r[j]);
        }
    }
}
int main()
{
    r[1]=1,r[2]=1,r[3]=2;
    cout<<"每個圓的半徑分別為:";
    for(int i=1;i<N;++i)
        cout<<r[i]<<' ';
    cout<<endl;
    backtrack(1);
    cout<<"最小圓排列長度為:"<<minlen<<endl;
    cout<<"最優圓排列的順序對應的半徑分別為:";
    for(int i=1;i<N;++i)
        cout<<bestr[i]<<' ';
    cout<<endl;
    return 0;
}
View Code

 時間復雜度

O(n) = n!


免責聲明!

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



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