问题描述
圆排列问题:给定n个圆的半径序列,将它们放到矩形框中,各圆与矩形底边相切,求具有最小排列长度的圆排列
解析
圆排列问题的解空间是一棵排列树。按照回溯法搜索排列树的算法框架,设开始时a=[r1,r2,……rn]是所给的n个元的半径,则相应的排列树由a[1:n]的所有排列构成。
如图,对于圆排列的问题,相邻的两个圆之间不一定会相切,如上方的第二个图。
如果有一个无限大的圆,那么这个矩形的长度就是这个圆的直径,如下图。
调用递归函数,如果搜索到达叶子节点,生成这种排列顺序并计算长度,否则,搜索下个排列的圆。找出所有序列中长度最短的序列输出
设计
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=4;
double minlen=10000,x[N],r[N];//分别为最小圆排列长度,每个圆心横坐标,每个圆半径
double minr[N];//最小圆排列的半径顺序
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)
minr[i]=r[i];
}
}
double get_location(int t)//求圆心坐标
{
double tmp=0;
for(int j=1;j<t;++j)
{
double xvalue=x[j]+2.0*sqrt(r[t]*r[j]);
if(xvalue>tmp)
tmp=xvalue;
}
return tmp;
}
void backtrack(int t)
{
if(t==N)
{
compute();
}
else
{
for(int j=t;j<N;j++)
{
swap(r[t],r[j]);
double get_locationx=get_location(t);
if(get_locationx+r[t]+r[1]<minlen)
{
x[t]=get_locationx;
backtrack(t+1);
}
swap(r[t],r[j]);
}
}
}
int main()
{
r[1]=2,r[2]=5,r[3]=4;
for(int i=1;i<N;++i)
cout<<"r"<<i<<"="<<r[i]<<' ';
cout<<endl;
backtrack(1);
cout<<"矩阵长度:"<<minlen<<endl;
cout<<"圆排列的顺序对应的半径分别为:";
for(int i=1;i<N;++i)
cout<<minr[i]<<' ';
cout<<endl;
return 0;
}
复杂度
T(n)=O(n^n)