用回溯法解决圆排列问题


教材是用的王晓东的《计算机算法设计与实现》第四版,c++版

一下是问题描述:

算法实现:

/**
*能确定一个正确的想法(即每种情况都能考虑到,然后找一个最简单而准确的方式表达出来)
*
**/ #include<iostream>//此问题没有要求相邻的圆必须相切


#include<math.h>
using namespace std;
class Circle{
 friend float CirclePerm(int,float*);//找到最优排列
 private:
  float Center(int t);//计算横坐标
  void Compute();//计算当前圆排列的长度
  void Backtrack(int t);
  float min,//最优值
  *x,//当前圆排列圆心横坐标
  *r; //当前元的半径
  int n;//当前待排园的个数
 
};
template <class T>
void Swap(T a,T b)
{
 T temp;
 temp = a;
 a = b;
 b = temp;
}
//此问题没有要求相邻的圆必须相切 ——对于计算横坐标,肯定选最大值
float Circle::Center(int t)//t代表当前选的第t个圆
{
 //计算当前所选园的圆心横坐标
 float temp = 0.0;//第一个圆默认为0
 //j表示第t个圆之前的圆,
 //x[j]这个数组第零个元素无用
 for(int j=1;j<t;j++)//为什么每次都从第一个圆开始比 ——找最大值,防止前一个与之相切的圆特别小 (不是,见下)
 {
  float valuex = x[j]+2*sqrt(r[t]*r[j]);//疑问①  考虑特殊情况 (如果特大圆放在第二个位置,圆心距与横坐标并没有错)——核心问题不是这个,这个两个之间也是相切的
  //另一种特殊情况是第二个圆特别小而第三个圆很大,导致一三相切二三不想切,这就是for循环的原因  
  //疑问二  不考虑特殊情况,为什么要加x[j]? —懂了,画图就明白了,x[j]是之前圆的横坐标
  if(valuex>temp)temp = valuex;
 } 
 return temp;
}
//负轴取绝对值最大,正轴取最大 ,n、

void Circle::Compute(void){//计算当前圆排列的长度,我的想法——(1--j)上所有圆的横坐标加上这个的圆心距取最小值(若最后一个圆特别小,这个公式根本不成立);课本上的想法——(负轴取绝对值最大,正轴取最大)
 float low = 0,
    high= 0;
    for(int i = 1;i<=n;i++)//n始终是不变的
    {
     if(x[i]-r[i]<low)low = x[i]-r[i];//为什么要积累low——也和特殊情况有关吗?             //这个算的就是第一个的半径吧,到底为什么要积累 ——不对,算的是负值的部分,若第一个特别小,第二个很大,则取最大值
     if(x[i]+r[i]>high)high = x[i]+r[i];//这个肯定要去最大值,若最后一个圆特别小,与边框根本不相切,算出来的值则不对
    }
    if(high-low<min)min = high-low;
}
void Circle::Backtrack(int t){
 if(t>n)Compute();
 else
 {
  for(int j=t;j<=n;j++)
  {
   Swap(r[t],r[j]);
   float centerx = Center(t);
   if(centerx+r[t]+r[1]<min){//下界约束
   x[t] = centerx;
   Backtrack(t+1);
   }
   Swap(r[t],r[j]);
  }
 }
}
float CirclePerm(int n,float*a)
{
 Circle X;
 X.n= n;
 X.r= a;
 X.min = 1000000;
 float*x = new float[n+1];
 X.x = x;
 X.Backtrack(1);
 delete[] x;
 return X.min;
}
int main()
{
 int n;
 float*a = new float[n+1];
 cin>>n;
 for(int i=1;i<=n;i++)
 {
  cin>>a[i];
 }
 cout<<CirclePerm(n,a)<<endl;
 return 0;                                                                                                                                                                                                            
}



注意:

算法部分来源于课本

此算法中的出来的是最优解

考虑到了相邻圆之间可能不相用回溯法解决圆排列问题切的情况


在看算法的过程中提了很多智障问题,都在注释里面,但也都自圆其说了,望大佬们如果发现有错误的,可以不吝赐教。