圆排列问题

问题

给定n个大小不等的圆c1,c2,…,cn,现要将这n个圆排进一个矩形框中,且要求各圆与矩形框的底边相切。圆排列问题要求从n个圆的所有排列中找出有最小长度的圆排列。
例如,当n=3,且所给的3个圆的半径分别为1,1,2时,这3个圆的最小长度的圆排列如图所示。其最小长度为2+4√2。
如图:
在这里插入图片描述

解析

圆排列问题的主要思路是排列问题,通过建立排列树,再进行回溯剪枝,得出最优排列。
每次修改一个圆的排列位置,若修改后的排列长度变小,则在当前排列的前提下继续排列,否则回溯。
每次排列后,相切情况下圆的X坐标计算公式:x2 = (r+ri)2 + (r-ri)2,
推出x = 2*sqrt(r+ri);r为自身半径,ri为相切圆半径。
考虑最坏情况,即x最大时,x+r0+r为当前最小圆排列长度。
当遍历完所有排列后,留下的就是最优圆排列。

在这里插入图片描述
排列共有ABC,ACB,BAC,BCA,CAB,CBA共六种。

算法

class Circle {
private:
	//圆总数
	int N;
	//最小圆排列长度
	double minlen = 100000;
	//各圆心横坐标
	double *x;
	//各圆半径
	double *r;
	//最小圆排列的半径顺序
	double *bestr;
	//计算最小圆排列长度
	void compute()
	{
		double low = 0, high = 0;
		for (int i = 0; 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 = 0; i < N; ++i)
				bestr[i] = r[i];
		}
	}
	double center(int t)//得到每个圆的圆心坐标
	{
		double temp = 0;
		double xvalue;
		for (int i = 0; i < t; ++i)
		{
			//随意取一圆相切,计算圆X坐标
			xvalue = x[i] + 2.0*sqrt(r[t] * r[i]);
			if (xvalue > temp) {
				temp = xvalue;
			}
		}
		return temp;
	}
	void backtrack(int t)
	{
		if (t == N)
		{
			compute();
		}
		else
		{
			//计算当前最优排列长度
			for (int i = t; i < N; ++i)
			{
				swap(r[t], r[i]);
				double centerx = center(t);
				//剪枝条件
				if (centerx + r[t] + r[0] < minlen)
				{
					x[t] = centerx;
					backtrack(t + 1);
				}
				swap(r[t], r[i]);//回溯,开始下一种排列
			}
		}
	}
public:
	Circle() {};
	Circle(int _N,  double *_r) {
		this->N = _N;
		this->x = (double *)malloc(sizeof(double)*_N);
		this->r = _r;
		this->bestr = (double *)malloc(sizeof(double)*_N);
	}
	void calculate() {
		backtrack(0);
		cout << "最小圆排列长度为:" << minlen << endl;
		cout << "最优圆排列的顺序对应的半径分别为:";
		for (int i = 0; i < N; ++i) {
			cout << bestr[i];
			if (i==N-1) {
				cout << endl;
			}
			else {
				cout << " ";
			}
		}
	}
};

计算结果

在这里插入图片描述
在这里插入图片描述

分析

如果不考虑计算当前园排列问题中各园圆的圆心横坐标和计算当前园排列长度所需的计算时间,则算法 backtrack需要O(n!)计算时间。由于算法 backtrack在最坏情况下可能需要O(n!)次当前园排列长度,每次计算需要O(n)计算时间,从而整个算法的计算时间复杂性为O((n+1)!)
上述算法尚有许多改进的地方。例如,像1,2,…,n-1,n和n,n-1,…2,1这种互为镜像的排列具有相同的圆排列长度,只计算一个就够了,可减少约一半的计算量。另一方面,如果所给的n个圆中有k个圆有相同的半径,则这k个圆产生的k!个完全相同的圆排列,只计算一个就够了。

git源码地址

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值