算法大作业 圆排列问题
問題
圓排列問題:給定n個圓的半徑序列,將它們放到矩形框中,各圓與矩形底邊相切,求具有最小排列長度的圓排列。
解析
圓排列問題的解空間是一棵排列樹。按照回溯法搜索排列樹的算法框架,設開始時a=[r1,r2,……rn]是所給的n個圓的半徑,則相應的排列樹由a[1:n]的所有排列構成。
1、compute函數可以想象其中任意的一個圓無限大或無限小,無限大的話那其余的圓就可以統統忽略了。因為已知所有圓的x坐標和半徑r,很容易求出每個圓的左右坐標,通過比較找出最小的左部坐標和最大的右部坐標,一減就是該圓排列的長度,然后把每次不同的排列長度相比較,找到更小的minlen就更新。
2、 center函數計算圓在當前圓排列中的橫坐標,由x^2 = sqrt((r1+r2)2-(r1-r2)2)推導出x = 2sqrt(r1r2)。注意排在任意位置的圓與其前或后的任意一個圓都有可能相切的,如圖所示。
3、back函數,這里用到的核心方法就是回溯法,回溯最重要的就是求出界限函數.按問題性質,可畫出子集樹或排列樹。if(cx+r[t]+r[1]<minlen)的作用是剪枝,先判斷當前層是否在范圍內,是則繼續搜索下一層,否則直接回溯。下圖是不進行剪枝時,六種可能性都搜索一遍的過程。
設計
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<cstdlib> #include<cmath> using namespace std; typedef long long ll; const int inf = 0x3f3f3f3f; double minlen, x[1010], r[1010]; int n; double best[1010]; 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++) {best[i] = r[i];}} }double center(int t) {double tem = 0;for (int i = 1; i < t; i++) {tem = max(x[i] + 2.0 * sqrt(r[i] * r[t]),tem);}return tem; }void back(int t) {if (t > n) compute();else {for (int i = t; i <= n; i++) {swap(r[t], r[i]);double cx = center(t);if (cx + r[1] + r[t] < minlen) {x[t] = cx;back(t + 1);}swap(r[t], r[i]);}} }int main() {scanf("%d", &n);for (int i = 1; i <= n; i++) scanf("%lf", &r[i]);minlen = inf;back(1);printf("最小排列長度:%f\n", minlen);for (int i = 1; i <= n; i++) {printf("%f", best[i]);if (i != n) printf("\n");} }分析
代碼的核心復雜度在back函數遞歸過程中,最壞情況下啊復雜度為O(n!),而每次遞歸的復雜度為O(n),所以總結得出復雜度為:
源碼
github源碼地址
總結
以上是生活随笔為你收集整理的算法大作业 圆排列问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 论文常用图表四:Bland-Altman
- 下一篇: 『ssh』使用shell远程执行命令