1. 程式人生 > >用回溯法解決圓排列問題

用回溯法解決圓排列問題

教材是用的王曉東的《計算機演算法設計與實現》第四版,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;                                                                                                                                                                                                            
}

注意:

演算法部分來源於課本

此演算法中的出來的是最優解

考慮到了相鄰圓之間可能不相切的情況

在看演算法的過程中提了很多智障問題,都在註釋裡面,但也都自圓其說了,望大佬們如果發現有錯誤的,可以不吝賜教。