1.问题
圆排列问题:给定n个圆的半径序列,将它们放到矩形框中,各圆与矩形底边相切,求具有最小排列长度的圆排列。
2.解析
给定n个圆c1,c2,…,cn,将这n个圆排进一个矩形框中,且要求各圆与矩形框的底边相切。圆排列问题要求从n个圆的所有排列中找出有最小长度的圆排列。
先输入的n个圆半径,接着计算出最小长度和当前选择的圆中心的横坐标,最后算排列。圆排列问题的解空间是一棵排列树。 设置函数来找到的最小的圆排列长度。开始时,数组a储存的n个圆的半径,计算返回最优解的圆排列,还需计算当前圆排列中的圆的横坐标,并计算当前圆排列的长度,接着找出当前最小圆排列长度。
3.设计
要把计算圆心坐标的公式放在一个for循环里面 , 排在任意位置的圆与其前或后的任意一个圆都有可能相切的 。在这之中要清楚目标圆有可能与排列中的任意一个圆相切,假设x[3]的坐标只能从前往后比较,先得到x[1]+a的值(即x[3]的可能坐标,再得到x[2]+b的值,与上一次的可能值相比较,若距离更大则更新,否则不边。
// 算计以后所择选圆的圆心横坐标
double cricle :: circle_center (int t)
{
double temp=0;
for (int j=1;j<t;j++)
{
// 算计以后所择选圆的圆心横坐标
double valuex=x[j]+2.0sqrt(r[t]r[j]);
if (valuex>temp)
{
temp=valuex;
}
}
return temp;
}
由x^2 = sqrt((r1+r2)2-(r1-r2)2)推导出x = 2sqrt(r1r2),
计算圆在当前圆排列中的横坐标:
double cricle :: circle_center (int t)
{
double temp=0;
for (int j=1;j<t;j++)
double valuex=x[j]+2.0*sqrt(r[t]*r[j]);//计算圆在当前圆排列中的横坐标:
if (valuex>temp)
{
temp=valuex;
}
return temp;
}
class cricle
{
friend double Cricle_p(int,double *);
private:
double circle_center (int t);
//后边圆的圆心心的横坐标
void Compute();
//以后的圆排列的度长
void back(int t);
double min,
*x,
//圆心横坐标
*r;
//圆排列
int n;
//圆的个数
};
// 算计圆排列的度长
void cricle ::Compute(void) // 算计圆排列的度长
{
double L ,H ;
for (int i = 1;i <= n; i++)
{
if (x[i]-r[i]<L)
{
L=x[i]-r[i];
}
if (x[i]+r[i]>H)
{
H=x[i]+r[i];
}
}
if (H-L<min)
{
min=H-L;
}
}
4.分析
最坏情况下需要计算O(n!)次圆排列长度,而起每次计算需要O(n)计算时间,所以平均时间复杂性为O((n+1)!)
时间复杂性为O((n+1)!)
5. 源码
#include
const int N = 100;
double Cricle_p(int n,double *a);
int main()
{
int n;
printf(“请输入圆的个数:” );
scanf("%d",&n);
double *a = new double[n+1];
printf(“请输入每个圆的半径:” );
for(int i=1; i<=n; i++)
{
scanf("%lf",&a[i]) ;
}
printf("请具有最小排列长度::" );
printf("%.3f",Cricle_p(3,a) );
return 0;
}
template
inline void swap1(T &a, T &b)
{
T t=a;
a=b;
b=t;
}
class cricle
{
friend double Cricle_p(int,double *);
private:
double circle_center (int t);
//后边圆的圆心心的横坐标
void Compute();
//以后的圆排列的度长
void back(int t);
double min,
*x,
//圆心横坐标
*r;
//圆排列
int n;
//圆的个数
};
double Cricle_p(int n,double *a)
{ double *cir_x = new double[n+1];
cricle coor;
coor.n = n;
coor.r = a;
coor.min = 1000;
coor.x = cir_x;
coor.back(1);
delete []cir_x;
return coor.min;
}
// 算计以后所择选圆的圆心横坐标
double cricle :: circle_center (int t)
{
double temp=0;
for (int j=1;j<t;j++)
{
double valuex=x[j]+2.0*sqrt(r[t]*r[j]);
if (valuex>temp)
{
temp=valuex;
}
}
return temp;
}
// 算计圆排列的度长
void cricle ::Compute(void)
{
double L ,H ;
for (int i = 1;i <= n; i++)
{
if (x[i]-r[i]<L)
{
L=x[i]-r[i];
}
if (x[i]+r[i]>H)
{
H=x[i]+r[i];
}
}
if (H-L<min)
{
min=H-L;
}
}
void cricle ::back(int t)
{
if (t>n)
{
Compute();
}
else
{
for (int j = t; j <= n; j++)
{
swap1(r[t], r[j]);
double centerx= circle_center (t);
if (centerx+r[t]+r[1]<min)
{
x[t]=centerx;
back(t+1);
}
swap1(r[t], r[j]);
}
}
}