大作业-圆排列问题

大作业-圆排列问题

问题

圆排列描述的是在给定n个大小不等的圆,C1,C2,C3,…,Cn,现要求将这n个圆排进一个矩形框中,且要求各圆与矩形框的底边相切。圆排列问题要求从n个圆的所有排列中找出有最小长度的圆的排列。
例如,给定3个圆,半径分别为1,2,1,圆排序后结果如下图所示
在这里插入图片描述

解析

圆排列问题的解空间是一棵排列树。按照回溯法搜索排列树的算法框架,设开始时a=[r1,r2,……rn]是所给的n个元的半径,则相应的排列树由a[1:n]的所有排列构成。

  1. calCircleCenter计算圆在当前圆排列中的横坐标,由x^ 2 = sqrt((r1+r2)^ 2-(r1-r2)^2)推导出x = 2*sqrt(r1 * r2)。
    把这个计算放在循环里,是因为后面的圆可能与前面任意一个圆相切,未必是往后面排列。
    如下图,如果是直接按照输入的顺序排列圆,
    在这里插入图片描述
    但是也可以这样相切
    在这里插入图片描述

  2. calCircleSortLength计算当前圆排列的长度。变量lenmin记录当前最小圆排列长度。数组r存储所有圆的半径。数组x则记录当前圆排列中各圆的圆心横坐标。
    我们可以想象其中任意的一个圆无限大或无限小,无限大的话那其余的圆就可以统统忽略了。因为已知所有圆的x[]和r[],很容易求出每个圆的左右坐标,通过比较找出最小的左部坐标和最大的右部坐标,一减就是该圆排列的长度,然后把每次不同的排列长度相比较,找到更小的minLen就更新。

  3. 在递归算法findCircleSort中,当i>n时,算法搜索至叶节点,得到新的圆排列方案。此时算法调用calCircleSortLength计算当前圆排列的长度,适时更新当前最优值。当i<n时,当前扩展节点位于排列树的i-1层。此时算法选择下一个要排列的圆,并计算相应的下界函数。
    在这里插入图片描述

设计

根据上述解析,首先写calCircleCenter函数。

   double calCircleCenter(int n){//计算圆心坐标 
	double maxx = 0;
	for(int i = 1;i < n;i++){
		int num = x[i] + 2.0 * sqrt(r[i] * r[n]);
		if(num > maxx)
			maxx = num;
	} 
	return maxx; 
}

calCircleSortLength函数

void calCircleSortLength(){//计算圆排列的总长度
	double low = 99999,high = 0;
	for(int i = 1;i <= CIRCLENUM;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 <= CIRCLENUM;i++){
			circleSeq[i] = r[i];
		}
	}
}

findCircleSort函数

void findCircleSort(int n){//查找圆排序
	if(n==CIRCLENUM + 1){
		calCircleSortLength();
	} 
	for(int i = n;i <= CIRCLENUM;i++){
		swap(r[n],r[i]);
		double center = calCircleCenter(n);//获取圆当前的横坐标
		x[n] = center;
		findCircleSort(n + 1);//向下继续搜索
		swap(r[n],r[i]);//回溯 
	}
}

分析

由排列组合可知,生成一个长度为n的序列的全排列的时间复杂度为O(n!)

同时在这个算法中,对于每一个排列中的每一个圆,它有可能和在它之前的任意一个圆相切,为了正确的确定是与那个圆相切需要使用for循环进行遍历,通过遍历找到与之相切的圆,其每次的时间复杂度为O(n)
计算最小圆排列的复杂度为O(n * n!)

源码

GitHub源代码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值