圆排列问题
1.问题
给定n个圆的半径序列,将它们放到矩形框中,各圆与矩形底边相切,求具有最小排列长度的圆排列。
2.解析
圆排列问题的解空间是一棵排列树。按照回溯法搜索排列树的算法框架,设开始时a=[r1,r2,……rn]是所给的n个元的半径,则相应的排列树由a[1:n]的所有排列构成。
- 定义一个函数center()来计算圆在当前圆排列中的横坐标,
- 定义一个函数compute()来计算当前圆的排列长度,找到排列的最左端 if(x[i]-r[i]<left) left=x[i]-r[i]; 找到排列的最右端if(x[i]+r[i]>right)计算出最右端 right=x[i]+r[i]; right-left就可以得到圆排列的长度。
- 变量lenmin记录当前最小圆排列长度。数组r存储所有圆的半径。数组x则记录当前圆排列中各圆的圆心横坐标。
- 在递归算法Backtrack中,当k>n时,算法搜索至叶节点,得到新的圆排列方案。此时算法调用Compute计算当前圆排列的长度,适时更新当前最优值。当k<n时,当前扩展节点位于排列树的k-1层。此时算法选择下一个要排列的圆,并计算相应的下界函数,还可以加入操作,若当前圆k的坐标center+r[1]+r[k]>=最小圆排列长度,就不需要继续遍历这个序列了。
3.设计
void backtrack(int k){
//查找圆排列
if(k>n){
//结束排列,计算排列长度
calLength();
return ;
}
for(int i=k;i<=n;i++){
swap(r[k],r[i]);
double center=calCenter(k);//获取圆当前的横坐标
if(center+r[1]+r[k]<minn){
//剪枝操作
x[k]=center;
backtrack(k+1);//向下继续搜索
}
swap(r[k],r[i]);//回溯
}
}
4.分析
由于算法Backtrack在最坏情况下需要计算O(n!)次圆排列长度,每次计算需要O(n)计算时间,得出时间复杂度为O((n+1)!)
空间复杂度为O(n)