圆排列问题


问题

圆排列问题:给定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