16回溯法——圆排列问题

article/2025/9/20 20:06:58

16基于回溯法的圆排列问题

目录

  • 16基于回溯法的圆排列问题
    • 1. 问题
    • 2. 解析
      • 举个栗子
    • 3. 设计
    • 4. 分析
    • 5. 源码

1. 问题

圆排列问题:给定n个圆的半径序列,将它们放到矩形框中,各圆与矩形底边相切,求具有最小排列长度的圆排列。

2. 解析

首先,对于n个圆的半径序列,我们将其全排列,并以树的形式展现。
这个树是多叉树,它满足:根节点的子结点数即为圆的个数,其后,随着树层数的增加,每后移一层,该层每个结点的子节点数会比前一层每个结点的子节点数减1,直至层结点的子结点数为0,问题的多叉树即构造完毕。

如下图,展现的是半径序列为{1,1,2}的排列多叉树:
在这里插入图片描述
由图很容易看出,根节点到每一个叶结点的追溯即是一种圆排列方式,因此当算法确定叶结点的位置后,我们就可以得出该叶结点所在的圆排列长度,经比较,便可以得到全局最短的圆排列长度及它的序列。
但是,我们都知道,当n稍大的时候,这棵树就会很庞大,算法效率也会很低。因此,我们采用了回溯法的思想,减少遍历情况,设立了一个界限函数——在前k(k<n)个结点排列都确定的情况下,计算加上第k个结点的排列长度,倘若该长度比已经计算得到的圆排列的最小长度还要长,就剪断分支,回溯父结点,不再遍历这第k个结点的子结点(因为k个圆的排列就已经比别的序列n个圆的排列长了,再继续下去到叶结点,肯定也不是全局最短圆排列长度);否则,继续。

基本思路已经理清了,倘若得到了一个完整排列,如何计算其长度呢?

  1. 计算该排列每个圆的圆心坐标
    若排列满足最短,则对于排列中的每一个圆,存在另外至少一个圆与之相切。(n>=2) (理解这句话非常重要)由于在该圆前面的圆序列及圆心坐标已经确定,我们就可以从中找到一个与所求圆相切的圆,并根据如下相切圆圆心横坐标距离公式,得到所求圆圆心在当前排列的横坐标位置。
    在这里插入图片描述
    因为所求圆不一定与排它前一个的圆相切(如下图所示),所以在getCenterX()中得到使其横坐标最右的坐标位置即可。注意,第一个圆的圆心横坐标为0.在这里插入图片描述
  2. 分别计算该排列左边界、右边界坐标,相减得到圆排列长度
    圆序列确定好了(叶结点确定),各圆心横坐标也计算好了,根据每个圆心横坐标及其半径计算它的左(右)边界坐标,众多圆中起始(末尾)位置最左(右)的就是该排列的左(右)边界横坐标,将左右边界横坐标相减,即为该排列的长度。
  3. 将得到的圆排列长度与界函数值minlength比较,比它小则更新minlength和最佳排列半径数组bestOrder[n].

举个栗子

给定n为4的圆半径序列为{1,1,2,4},其最短圆排列为{2,1,4,1}.
在这里插入图片描述

3. 设计

void traceBack(int k,int num) {if (k == num) then//抵达叶子,得到当前排列方式的排列长度getLength(num);else for (j = k to num-1)swap(radius[k], radius[j]);//得到圆k的圆心坐标nowX ← getCenterX(k,num);//剪枝条件if (nowX + radius[k] + radius[0] < minlength) thencenterX[k] ← nowX;traceBack(k + 1,num);end if//回溯结点swap(radius[k], radius[j]);end forend if
}

4. 分析

该算法在最坏情况下可能需要O(n!)次计算当前圆排列长度,也就是将每种情况都考虑了一遍(全排列),而每次又需要 O(n) 的计算时间,从而整个算法的计算时间复杂度为traceBack(0,n)=O((n+1)!).
如果圆半径呈镜像排列,traceBack(0,n)=O((n+1)!/2).
若圆半径一样,traceBack(0,n)=O(1).

5. 源码

点击前往Git

在这里插入图片描述

#include<iostream>
#include<cmath>
#define N 200using namespace std;
//半径	圆心横坐标	最短圆排列
double radius[N], centerX[N],bestOrder[N];
//最短圆排列长度
double minlength = 0xffffff;//得到每个圆的圆心位置
double getCenterX(int k,int num) {double tmp = 0;//排列最短必定存在一个圆与该圆相切,找到一个与之相切的圆即可得到该圆坐标for (int i = 0; i < k; i++) {//相切圆圆心横坐标距求法double value = centerX[i] + 2.0 * sqrt(radius[k] * radius[i]);if (value > tmp) {tmp = value;}}return tmp;
}//得到当前圆排列长度
void getLength(int num) {double left = 0xffff, right = -0xffff;for (int i = 0; i < num; i++) {//众多圆中起始位置最左的就是该排列的左边界if (centerX[i] - radius[i] < left) {left = centerX[i] - radius[i];}//众多圆中末尾位置最右的就是该排列的右边界if (centerX[i] + radius[i] > right) {right= centerX[i] + radius[i];}}//判断该排列是否为已计算排列中长度最小的,是则更新最小排列长度if (right - left < minlength) {minlength = right - left;//并更新最小圆排列顺序for (int i = 0; i < num; i++) {bestOrder[i] = radius[i];}}
}void traceBack(int k,int num) {//全部圆都已参与排列,计算该排列长度if (k == num) {getLength(num);}else {for (int j = k; j < num; ++j){swap(radius[k], radius[j]);double nowX = getCenterX(k,num);//剪枝条件if (nowX + radius[k] + radius[0] < minlength){centerX[k] = nowX;traceBack(k + 1,num);}//回溯swap(radius[k], radius[j]);}}
}int main() {int n;while (1) {minlength = 0xffffff;cout << "请输入圆的数量:";cin >> n;if (n == 0) {break;}cout << "请分别输入"<<n<<"个圆的半径:";for (int i = 0; i < n; i++) {cin >> radius[i];}traceBack(0,n);cout <<"最小圆排列长度为:" << minlength << endl;cout << "最优圆排列的顺序对应的半径分别为:";for (int i = 0; i < n; ++i)cout << bestOrder[i] << ' ';cout << endl<<endl;}return 0;
}

http://chatgpt.dhexx.cn/article/qUZkmxb0.shtml

相关文章

五分钟解决圆排列问题

给定n个大小不等的圆c1,c2,…,cn&#xff0c;现要将这n个圆排进一个矩形框中&#xff0c;且要求各圆与矩形框的底边相切。圆排列问题要求从n个圆的所有排列中找出有最小长度的圆排列。例如&#xff0c;当n3&#xff0c;且所给的3个圆的半径分别为1&#xff0c;1&#xff0c;2时…

用回溯法解决圆排列问题

教材是用的王晓东的《计算机算法设计与实现》第四版&#xff0c;c版 一下是问题描述&#xff1a; 算法实现: /***能确定一个正确的想法&#xff08;即每种情况都能考虑到&#xff0c;然后找一个最简单而准确的方式表达出来&#xff09; ***/ #include<iostream>//此问题…

回溯法之圆排列问题

问题描述 给定n个大小不等的圆c1,c2,…,cn&#xff0c;先要将这n个圆排进一个矩形框中&#xff0c;且要求各圆与矩形框的底边相切。圆排列问题要求从n个圆的所有排列中找出有最小长度的圆排列。例如&#xff0c;当n3时&#xff0c;且所给的3个圆的半径分别为1、1、2时&#xf…

5-10 圆排列问题(回溯)

5-10 圆排列问题(回溯) 给定n个大小不等的圆c1, c2,…, cn&#xff0c;现要将这n个圆排进一个矩形框中&#xff0c;且要求各圆与矩形框的底边相切。圆排列问题要求从n个圆的所有排列中找出有最小长度的圆排列。 例如&#xff0c;当n3&#xff0c;且所给的3个圆的半径分别为1&a…

【2019华为笔试】召唤师的技能——圆排列,翻转和项链排列

题目描述&#xff1a; dota游戏里面&#xff0c;召唤师可以控制冰雷火三种元素&#xff0c;并通过元素组合产生新的技能。现在我们修改了张新的地图&#xff0c; 地图中他能够控制n种元素&#xff0c; 并且将m个元素围成一个圈组成一 个新技能(这m个元素通过旋转或反转&#x…

圆排列问题

问题 圆排列问题&#xff1a;给定n个圆的半径序列&#xff0c;将它们放到矩形框中&#xff0c;各圆与矩形底边相切&#xff0c;求具有最小排列长度的圆排列。 解析 首先对于这个问题&#xff0c;使用分支限界计算&#xff0c;一定会遍历所有的排列情况&#xff0c;剪枝就是当前…

图文并茂详尽剖析圆排列问题

参考资料 https://blog.csdn.net/liufeng_king/article/details/8890603https://blog.csdn.net/qq_32400847/article/details/51474105https://blog.csdn.net/yzmck/article/details/4302554 原理解释的很赞http://www.doc88.com/p-079198350775.html http://www.docin.com/p-…

圆排列问题详解(原理+代码)

问题描述 给定n个大小不等的圆c1,c2,…,cn&#xff0c;现要将这n个圆排进一个矩形框中&#xff0c;且要求各圆与矩形框的底边相切。圆排列问题要求从n个圆的所有排列中找出有最小长度的圆排列。 问题分析 圆排列问题的解空间是一棵排列树&#xff0c;我们用回溯法在整个排列…

java字节序、主机字节序和网络字节序扫盲贴

java程序员是幸福&#xff0c;因为相对于C/C的不跨平台&#xff0c;JVM为我们屏蔽了大量的底层细节和复杂性&#xff0c;让我们能够将精力放在实现特定的业务逻辑上&#xff0c;所以使用java开发项目效率是比较高的。同时java程序员是悲哀的&#xff0c;就是因为JVM屏蔽了很多技…

字节序、位序

字节序 字节序&#xff0c;又称端序、尾序&#xff0c;英文单词为Endian&#xff0c;该单词来源于于乔纳森斯威夫特的小说《格列佛游记》&#xff0c;小说中的小人国因为吃鸡蛋的问题而内战&#xff0c;战争开始是由于以下的原因&#xff1a;我们大家都认为&#xff0c;吃鸡蛋前…

字节序的详细讲解

字节序 1、字节序的特点2、字节序转换函数2.1、htonl函数 发 将主机字节序的IP地址 转换成网络字节序的IP地址2.2、ntohl函数 收 将网络字节序的IP地址3.3、htons函数 发 将主机字节序的端口 转换成 网络字节序的端口3.4、ntohs函数 收 将网络字节序的端口 转换成 主机字节序的…

理解字节序 大端字节序和小端字节序

以下内容参考了 http://www.ruanyifeng.com/blog/2016/11/byte-order.html https://blog.csdn.net/yishengzhiai005/article/details/39672529 1. 计算机硬件有两种储存数据的方式&#xff1a;大端字节序&#xff08;big endian&#xff09;和小端字节序&#xff08;little …

什么是字节序?

字节序 字节序&#xff0c;顾名思义&#xff0c;就是字节组织的顺序。我们可以将其根据其存储时从低位开始还是从高位开始分为两种&#xff0c;具体如下&#xff1a; 类型简写本质大端BE(big endian)将高序字节存储在起始地址小端LE(little endian)将低序字节存储在起始地址 …

网络字节序和主机字节序详解(附代码)

一、网络字节序和主机字节序 网络字节序和主机字节序是计算机网络中常用的两种数据存储格式。 主机字节序&#xff1a; 指的是在计算机内部存储数据时采用的字节排序方式。对于一个长为4个字节的整数&#xff0c;若采用大端字节序&#xff0c;则该整数在内存中的存储顺序是&a…

字节序

1.字节序 字节序&#xff0c;又称端序或尾序&#xff0c;指的是多字节数据在内存中的存放顺序。例如一个int型变量x占用4个字节&#xff0c;假设它的起始地址&x为0x10&#xff0c;那么x将会被存储在 0x10、0x11、0x12和0x13位置上。 在用C写的客户端和Java写的服务端的通…

字节序详细解读

概念来了&#xff01;&#xff01;&#xff01; 字节序&#xff08;Byte Order&#xff09;是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。 在计算机中是以字节为单位&#xff0c;每个地址对应一个字节&#xff0c;一个字节8bit。在C中&#xff0c;除了8bi…

JitPack的简单使用

JitPack的简单使用 由于工作需要,我要搭建多个项目,但是每个项目的基类,工具包,自定义的view,都是一样的,需要将这些代码复制到好几个项目里,所以萌生了一个想法,将这些基本不会改变的代码,做成一个依赖,一行代码引入项目 打开你的项目Git地址,创建发行版本 打开jitpack官网…

Gitee项目发布到JitPack

Gitee项目发布到JitPack 发布前的准备将Gitee项目发布到jitPack使用自己发布的库 发布前的准备 1.搜先注册一个Gitee账号 2.新建一个本地的android项目&#xff0c;并上传到Gitee。 (1) 根目录下build.gradle配置 dependencies {...classpath "com.android.tools.build:g…

【jitpack】android implementation 远程仓库

目录 前言接入步骤使用说明问题后记结束语 前言 做android的小伙伴都知道&#xff0c;android studio 在使用其他三方项目的时候使用gradle来管理版本,如直接使用如下就能快速把 appcopmt-v7 引入到项目中使用 implementation com.android.support:appcompat-v7:28.0.0 其实…

【JitPack】发布一个你自己的 Android 依赖库

文章目录 背景步骤新建 Android Library 项目配置 Gradle提交项目到 githubPull Request 到主分支创建 tag 并发布 release 版本JitPack 生成项目的依赖库第三方应用集成使用依赖库 JitPack 生成依赖库的 pom.xml 文件地址参考链接 背景 最近突然想开发个 Android Library&…