1,问题
圆排列问题:给定n个圆的半径序列,将它们放到矩形框中,各圆与矩形底边相切,
求具有最小排列长度的圆排列
2,解析
主要利用回溯法来解决该问题。
r[n]: 记录每个圆的半径
x[n]: 记录每个圆的横坐标
minlen: 圆排列最小长度
函数 center(n): 求第n个圆的横坐标
函数 compute(): 计算当前r[n]的排列的长度
backtrack(t): t<n, 遍历排列树t层,更新minlen.
3,设计
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]);
}
}
}
利用递归来遍历序列树的每一层,利用循环来遍历每一层的每一种情况,最终得到最短长度。
4,分析
算法时间复杂度:O(n!)
5,源码
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
const int N = 4;
double minlen =