圆排列问题

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
代码解读及食用方法:
已放在源代码注释内

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值