1.问题
圆排列问题:给定n个圆的半径序列,将它们放到矩形框中,各圆与矩形底边相切,求具有最小排列长度的圆排列。
2.解析
但是不是每个排列都像只有两个圆需要排序时那么简单,当需要排序的圆变多的时候就会形成一个排序问题,此时我们可以这样解决问题。
1.计算圆在当前圆排列中的横坐标,由cx2 = sqrt((r1+r2)2-(r1-r2)2)推导出cx = 2*sqrt(r1*r2)。
2.计算当前圆排列的长度。新增一个变量记录当前最小圆排列长度,以及两个数组分别用于存储所有圆的半径和记录当前圆排列中各圆的圆心横坐标。
3.设计一个递归算法,当节点大于n时,算法搜索至叶节点,得到新的圆排列方案。此时计算当前圆排列的长度,适时更新当前最优值。当节点小于n时,当前扩展节点位于当前节点的上一层。接着选择下一个要排列的圆,并计算相应的下界函数。
3.设计
圆心横坐标计算 {
Len=0;
for (i=1 to c) {
t = cx[i] + 2 * sqrt((r[c] * r[i]));
if (t > len)
len = t;
}
return len;
}
当前排列长度计算{
L = 0, R = 0;
for (i=1 to n) {
if (cx[i] - r[i] < L)
L = cx[i] - r[i];
if (cx[i] + r[i] > R)
R = cx[i] + r[i];
}
if ((R - L) <= minn) {
minn = R - L;更新minn值
for (i=1 to n)记录顺序
a[i] = r[i];
}
}
回溯函数{
if (c == n + 1)
cal();计算当前排列长度
else {
for (i=c to n) {
swap(r[c], r[i]);全排列
x = getcx(c);x为c圆心坐标
if (r[1] + x + r[c] < minn) {
cx[c] = x;
dfs(c + 1);继续回溯
}
swap(r[c], r[i]);顺序调换
}
}
}
4.分析
这应该算是一个O(N^2)的问题。
5.源码
#include<map>
#include<stdlib.h>
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<cmath>
#include<string>
#include<cstdio>
#include <iomanip>
using namespace std;
//const int maxn = 3e5 + 10;
#define ll long long
int i, j, k;
int n, m;
const int inf = 0x3f3f3f;
const int mod = 1e9 + 7;
map<ll, ll> mpp[30];
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a%b);
}
const int maxn = 1010;
double r[maxn], cx[maxn], a[maxn];
float minn = inf;
double getcx(int c) {
double len = 0, t;
for (int i = 1; i < c; i++) {
t = cx[i] + 2 * sqrt((r[c] * r[i]));
if (t > len)
len = t;
}
return len;
}
void cal() {//计算当前圆排列的长度
double L = 0, R = 0;
for (int i = 1; i <= n; i++) {
if (cx[i] - r[i] < L)
L = cx[i] - r[i];
if (cx[i] + r[i] > R)
R = cx[i] + r[i];
}
if ((R - L) <= minn) {
minn = R - L;//更新最小值
for (i = 1; i <= n; i++)//记录最优顺序
a[i] = r[i];
}
}
void dfs(int c) {
if (c == n + 1)//最后一个圆已经调整完毕
cal();//计算当前圆排列长度
else {
for (int i = c; i <= n; i++) {
swap(r[c], r[i]);
double x = getcx(c);//得到圆c的圆心横坐标
if (r[1] + x + r[c] < minn) {
cx[c] = x;
dfs(c + 1);
}
swap(r[c], r[i]);
}
}
}
int main() {
cin >> n;//圆的个数
for (i = 1; i <= n; i++)
cin >> r[i];//每个圆的半径
dfs(1);
cout << "length: " << minn << endl;
cout << "sort: ";
for (i = 1; i <= n; i++) {
cout << a[i] << " ";
}
puts("");
return 0;
}
Github:
https://github.com/myycjw/circle
代码解读及食用方法:
已放在源代码注释内