圆排列问题

1.问题描述

给定n个大小不等的圆c1,c2,…,cn,现要将这n个圆排进一个矩形框中,且要求各圆与矩形框的底边相切。圆排列问题要求从n个圆的所有排列中找出有最小长度的圆排列。例如,当n=3,且所给的3个圆的半径分别为1、1、2时,这3个圆的最小长度的圆排列如图,其最小长度为2+4√2。
在这里插入图片描述

2.算法分析

该问题是一个排列问题,问题的解存储在序列r={r1,r2,r3…}中,问题的解空间树是一颗排列树。用x={x1,x2,x3…}表示当前排列的各个圆的圆心。

  • 下界函数:用Center(t)计算新增圆的圆心,每次新增一个圆则排列的序列会增长,比较center(t)+r[t]+r[0]这个值(注意不是序列中总长,只是t号圆的最右端的长度)和已知序列长度min比较,如果大于min则剪枝。
  • 实现:遍历r[0:t-1]序列,假设新增圆与其相切,(rt+rj)^2-(rt-rj)^2=4*rj*rt=>x[t]=x[i]+2.0*sqrt(r[t]*r[i])
    在这里插入图片描述
float center(int t)//计算t圆的圆心x坐标
{
    float res=0;
    for(int i=0;i<t;i++)
    {//(rt+rj)^2-(rt-rj)^2=4*rj*rt
        float temp=x[i]+2.0*sqrt(r[t]*r[i]);
        if(temp>res) res=temp; 
    }
    return res;
}
  • 用compute计算整个序列的长度,遍历每一个圆,记录左端low和右端high,迭代得到low的最小值和hight的最大值,最后做差即可得到序列长度。
    在这里插入图片描述
void compute()
{
    float low=0,high=0;
    for(int i=0;i<n;i++)
    {
        float tempLow=x[i]-r[i];
        float rempHigh=x[i]+r[i];
        if(tempLow<low) low=tempLow;
        if(rempHigh>high) high=rempHigh;
    }
    if(high-low<minx) minx=high-low;
}

3.算法实现

#include <iostream>
#include <math.h>
using namespace std;
const int maxn=100005;
int n;
float minx=1e7;
float x[maxn]={0},r[maxn]={0};
float center(int t)//计算t圆的圆心x坐标
{
    float res=0;
    for(int i=0;i<t;i++)
    {//(rt+rj)^2-(rt-rj)^2=4*rj*rt
        float temp=x[i]+2.0*sqrt(r[t]*r[i]);
        if(temp>res) res=temp; 
    }
    return res;
}
void compute()
{
    float low=0,high=0;
    for(int i=0;i<n;i++)
    {
        float tempLow=x[i]-r[i];
        float rempHigh=x[i]+r[i];
        if(tempLow<low) low=tempLow;
        if(rempHigh>high) high=rempHigh;
    }
    if(high-low<minx) minx=high-low;
}
void circleBacktrace(int t)
{
    if(t>=n) compute();//更新minx
    else{
        for(int i=t;i<n;i++)
        {
            swap(r[t],r[i]);
            float c=center(t);//圆心
            if(c+r[t]+r[0]<minx)
            {
                x[t]=c;
                circleBacktrace(t+1);
            }
            swap(r[t],r[i]);
        }
    }
}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>r[i];
    circleBacktrace(0);
    printf("The min arrengment is %f\n",minx);
    system("pause");
    return 0;
}
/*
3
1 1 2
*/

4.算法复杂度

  • 时间复杂度:对于排列树,共有n!次计算;另外在每次计算圆心的过程中有O(n)次计算时间,因此整体算法时间复杂度为T(n)=O((n+1)!)
  • 空间复杂度:S(n)=O(n)
原创文章 236 获赞 430 访问量 7万+

猜你喜欢

转载自blog.csdn.net/weixin_44307065/article/details/106089727