回溯法--旅行售货员问题--排列树

news2025/1/20 15:41:57

回溯法有点类似于暴力枚举的搜索过程,回溯法的基本思想是按照深度优先搜索的策略,从根节点出发深度搜索解空间树,当搜索到某一节点时,如果该节点可能包含问题的解,则继续向下搜索;反之回溯到其祖先节点,尝试其他路径搜索。

第一类问题:只要求求得一个可行解,那么搜索到问题的一个解即可结束;

第二类问题:求最优解,那么需要搜索整个解空间树,得到所有解之后择最优作为问题的解。

回溯法与暴力搜索的区别:在搜索到叶子节点之前已经能确定该路径不为最优解时就可以进行剪枝,节省搜索时间。

回溯法有两种模板:子集树和排列树。旅行售货员问题时典型的排列树。

子集树: 所给的问题是从n个元素的集合中找出满足某种性质的子集时, 相应的解空间称为子集树.
  子集树通常有2^n个叶结点, 遍历子集树的任何算法均需Ω(2^n)的计算时间.

  例如: 0-1背包问题的解空间为一棵子集树.

排列树: 当所给的问题是确定n个元素满足某种性质的排列时, 相应的解空间称为排列树.
  排列树通常有(n-1)!个叶结点, 遍历排列树需要Ω(n!)的计算时间.

  例如: 旅行售货员问题的解空间为一棵排列树.

问题描述

 某售货员要到若干城市去推销商品,已知各城市之间的路程,他要选定一条从驻地出发,经过每个城市一遍,最后回到住地的路线,使总的路程最短。

 

 由于只有4个城市,如果规定售货员总是从城市1出发,那么依据排列组合可以得到6种不同的旅行方案,比如12341、13241等等。在这些排列组合基础上可以很容易绘制出一棵排列树,也是该问题的解空间树,排列树如下:

 我们可以得到如下信息:

根据解空间树可以得到一些有用的信息:

  • 该树的深度为5
  • 两个节点之间路径上的标识数组代表所走城市
  • 树上没有体现出回到城市1的路径,但实际上计算要考虑这段路程

问题分析

 

假设起点为 1。

算法开始时 x = [1, 2, 3, …, n]

x[1 : n]有两重含义 x[1 : i]代表前 i 步按顺序走过的城市, x[i + 1 : n]代表还未经过的城市。利用Swap函数进行交换位置。

i = n 时,处在排列树的叶节点的父节点上,此时算法检查图G是否存在一条从顶点x[n-1] 到顶点x[n] 有一条边,和从顶点x[n] 到顶点x[1] 也有一条边。若这两条边都存在,则发现了一个旅行售货员的回路(即:新旅行路线),算法判断这条回路的费用是否优于已经找到的当前最优回路的费用bestcost,若是,则更新当前最优值bestcost和当前最优解bestx。

i < n 时,检查x[i - 1]至x[i]之间是否存在一条边, 若存在,则x [1 : i ] 构成了图G的一条路径,若路径x[1: i] 的耗费小于当前最优解的耗费,则算法进入排列树下一层,否则剪掉相应的子树。

伪代码

定义变量:图的基本信息(邻接矩阵、顶点数、边数),当前解,最优解,标记有边无边(方便初始化和判断)

 回溯主函数

 

代码

//旅行商问题--排列树
#include<iostream>
#include<cstring>
using namespace std;
#define maxn 0x3f3f3f
const int NoEdge = -1;      //两个点之间没有边
int citynum;                //城市数
int edgenum;                //边数
int currentcost;            //记录当前的路程
int bestcost;               //记录最小的路程(最优)
int Graph[100][100];        //图的边距记录
int x[100];                 //记录行走顺序
int bestx[100];             //记录最优行走顺序

void InPut(){
    int pos1, pos2, len;     //点1 点2 距离

    cout<<"请输入城市数和边数(c e):";
    cin>>citynum>>edgenum;

    memset(Graph, NoEdge, sizeof(Graph));

    cout<<"请输入两座城市之间的距离(p1 p2 l):"<<endl;
    for(int i = 1; i <= edgenum; ++i)
    {
        cin>>pos1>>pos2>>len;
        Graph[pos1][pos2] = Graph[pos2][pos1] = len;
    }
}

//初始化
void Initilize(){
    currentcost = 0;
    bestcost = maxn;
    //初始化行走顺序,第i步走的城市
    for(int i = 1; i <= citynum; i++) x[i] = i;
}


void Swap(int &a, int &b){
    int temp;
    temp = a;
    a = b;
    b = temp;
}


void BackTrack(int i) //这里的i代表第i步去的城市而不是代号为i的城市
{
    if(i == citynum)
    {
        //进行一系列判断,注意的是进入此步骤的层数应是叶子节点的父节点,而不是叶子节点
        if(Graph[x[i - 1]][x[i]] != NoEdge && Graph[x[i]][x[1]] != NoEdge && (currentcost + Graph[x[i - 1]][x[i]] + Graph[x[i]][x[1]] < bestcost || bestcost == maxn))
        {
            //最小(优)距离=当前的距离+当前城市到叶子城市的距离+叶子城市到初始城市的距离
            bestcost = currentcost + Graph[x[i - 1]][x[i]] + Graph[x[i]][x[1]];
            for(int j = 1; j <= citynum; ++j)
                bestx[j] = x[j];
        }
    }
    else
    {
        for(int j =  i; j <= citynum; ++j)
        {
            if(Graph[x[i - 1]][x[j]] != NoEdge && (currentcost + Graph[x[i - 1]][x[j]] < bestcost || bestcost == maxn))
            {
                Swap(x[i], x[j]);  //这里i 和 j的位置交换了, 所以下面的是currentcost += Graph[x[i - 1]][x[i]];
                currentcost += Graph[x[i - 1]][x[i]];
                BackTrack(i + 1);   //递归进入下一个城市
                currentcost -= Graph[x[i - 1]][x[i]];
                Swap(x[i], x[j]);
            }
        }
    }
}

void OutPut()
{
    cout<<"最短路程为:"<<bestcost<<endl;
    cout << "路线为:" << endl;
    for(int i = 1; i <= citynum; ++i)
        cout << bestx[i] << " ";
    cout << "1" << endl;
}


int main()
{
    InPut();
    Initilize();
    BackTrack(2);
    OutPut();
}
/*假设起点为 1。
算法开始时 x = [1, 2, 3, …, n]
x[1 : n]有两重含义 x[1 : i]代表前 i 步按顺序走过的城市, 
x[i + 1 : n]代表还未经过的城市。利用Swap函数进行交换位置。
i = n 时,处在排列树的叶节点的父节点上,
此时算法检查图G是否存在一条从顶点x[n-1] 到顶点x[n] 有一条边,
和从顶点x[n] 到顶点x[1] 也有一条边。若这两条边都存在,
则发现了一个旅行售货员的回路(即:新旅行路线),
算法判断这条回路的费用是否优于已经找到的当前最优回路的费用bestcost,
若是,则更新当前最优值bestcost和当前最优解bestx。
i < n 时,检查x[i - 1]至x[i]之间是否存在一条边, 
若存在,则x [1 : i ] 构成了图G的一条路径
若路径x[1: i] 的耗费小于当前最优解的耗费,
则算法进入排列树下一层,否则剪掉相应的子树。*/

以前面的样例示范

输入

请输入城市数和边数(c e):4 6
请输入两座城市之间的距离(p1 p2 l):
1 2 30
1 3 6
1 4 4
2 4 10
2 3 5
3 4 20

输出
最短路程为:25
路线为:
1 3 2 4 1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/161356.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【学习笔记】【Pytorch】六、nn.Module的使用

【学习笔记】【Pytorch】六、nn.Module的使用学习地址主要内容一、torch.nn模块概述二、nn.Module类的使用1.使用说明2.代码实现学习地址 PyTorch深度学习快速入门教程【小土堆】. 主要内容 一、torch.nn模块概述 概述&#xff1a;帮助程序员方便执行与神经网络相关的行为。…

微信小程序——WXSS模板样式,rpx,样式导入,全局样式和局部样式,全局配置-window

一.WXSS模板样式1.什么是 wxss WXSS ( WeiXin Style Sheets &#xff09;是一套样式语言&#xff0c;用于美化 WXML 的组件样式&#xff0c;类似于网页开发中的CSS。2. WXSS 和 CSS 的关系WXSS 具有 CSS 大部分特性&#xff0c;同时&#xff0c; WXSS 还对 CSS 进行了扩充以及修…

73. 风格迁移以及代码实现

摄影爱好者也许接触过滤波器。它能改变照片的颜色风格&#xff0c;从而使风景照更加锐利或者令人像更加美白。但一个滤波器通常只能改变照片的某个方面。如果要照片达到理想中的风格&#xff0c;可能需要尝试大量不同的组合。这个过程的复杂程度不亚于模型调参。 本节将介绍如…

数据导入导出(POI以及easyExcel)

一.概念&#xff1a; 1.场景需求 将一些数据库信息导出为Excel表格 将Excel表格数据导入数据库 大量数据的导入导出操作 常⽤的解决⽅案为&#xff1a;Apache POI与阿⾥巴巴easyExcel2.Apache POI介绍 Apache POI 是基于Office Open XML 标准&#xff08;OOXML&#xff09;和M…

96、【树与二叉树】leetcode ——404. 左叶子之和:递归法[先序+后序]+迭代法[先序+层次](C++版本)

题目描述 原题链接&#xff1a;404. 左叶子之和 解题思路 一、递归法 &#xff08;1&#xff09;设置一个布尔变量判定&#xff08;先序遍历&#xff09; 左孩子一定在每个子树的最左侧&#xff0c;因此设置一个局部参数flag&#xff0c;当每次遍历的是左子树时&#xff0c…

记录一次Oracle Linux7上安装RDBMS 11.2.0.4的问题

参考文档&#xff1a; 文档1 OHASD fails to start on SuSE 11 SP2 on IBM: Linux on System z (Doc ID 1476511.1) As part of the root.sh, roothas.pl or rootcrs.pl is called and an entry is placed in /etc/inittab h1:35:respawn:/etc/init.d/init.ohasd run >/de…

cmake入门之二:调用外部共享库和头文件

cmake入门之二&#xff1a;调用外部共享库和头文件1.创建外部共享库1.1 创建相关文件或文件夹1.1.1 ext目录下的CMakeLists.txt1.1.2 ext目录lib文件夹下1.1.2.1 hello.h1.1.2.2 hello.c1.1.2.3 CMakeLists.txt1.2 编译、安装2.创建当前项目2.1 创建相关文件或文件夹2.1.1 proj…

为什么这么努力,还是赚不到钱?你不能不知道赚钱的三个模型

大部分人赚钱&#xff0c;都是通过能力努力运气&#xff0c;这种需要付出自己的大量时间和精力&#xff0c;并且赚到的钱也是有上限的。小部分人赚钱是通过&#xff0c;个人IP的商业模式来赚钱&#xff0c;并且跟我们传统的生意不一样的是&#xff0c;这个亏钱是有下限的&#…

OpenHarmony轻量级设备xts认证大致流程

因为最近公司在做openharmony开发板xts认证&#xff0c;这里对认证过程和过程中遇到的坑做下记录&#xff0c;也给大家探探路。 1. 开发板适配 OpenHarmony轻量系统的移植比较简单&#xff0c;代码中解耦做得非常好。从代码的设计理念上来看&#xff0c;移植主要是3部分的内容…

.vscode/extensions.json和setting.json 是项目用到的插件推荐列表和设置

文章目录前言一、extensions.json安装推荐插件编辑推荐插件二、setting.json总结前言 在前端项目&#xff0c;文件目录中存在.vscode文件夹&#xff0c;文件夹下一般存在两个文件extensions.json和setting.json。作用是保持所有开发者安装了相同的插件和相同的配置&#xff0c…

访问学者J1签证通常准备那些材料?

访问学者J1签证通常准备那些材料&#xff1f;知识人网小编马上整理一下分享出来作为参考&#xff1a;材料准备1、VISA部分&#xff1a;护照&#xff0c;护照照片&#xff0c;160确认页&#xff0c;签证费收据两联都带&#xff0c;DS2019&#xff0c;D7002&#xff0c;sevis费收…

Linux应用基础——监控与管理进程

目录 一、进程 1.定义 2.组成 3.进程环境包括 4.进程的生命周期 二、描述进程状态 三、相关命令 1.top命令 2.ps命令 二、中断进程 1.基本进程管理信号 2.每个信号的默认操作 3.相关命令 &#xff08;1&#xff09;kill命令 &#xff08;2&#xff09;killall命…

助力不文明行文识别,基于YOLOv7融合RepVGG的遛狗牵绳行为检测识别分析系统

不知道大家平时在路上走的时候或者在小区的时候有没有遇上过遛狗不牵绳子的行为&#xff0c;我在实际生活里面可是没少遇到过&#xff0c;有时候特别大的一只狗就这么冲过来&#xff0c;主人却还无动于衷&#xff0c;揍他的心都有了&#xff0c;这种行为的确是很不文明&#xf…

Java项目:仓库管理系统设计和实现(java+ssm+springboot+layui)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 主要功能模块 1.用户模块管理&#xff1a;用户登录、用户注册、用户的查询、添加、删除操作、 2.客户信息管理&#xff1a;.客户列表的展示、添加、修改、删除操作、 3.供应商管理&#xff1a;供应商详情…

Android MVVM之ViewModel的详解与使用

一、介绍 ViewModel 类是一种业务逻辑或屏幕级状态容器。它用于将状态公开给界面&#xff0c;以及封装相关的业务逻辑。 它的主要优点是&#xff0c;它可以缓存状态&#xff0c;并可在配置更改后持久保留相应状态。这意味着在 activity 之间导航时或进行配置更改后&#xff08;…

【UE4 第一人称射击游戏】45-使用线追踪进行破坏

上一篇&#xff1a;【UE4 第一人称射击游戏】44-瞄准时的武器线追踪步骤&#xff1a;打开“Weapon_Base”删除打印节点添加如下节点&#xff0c;表示追踪线命中目标时执行的逻辑对上面逻辑的解释&#xff1a;首先追踪线命中目标后&#xff0c;显示红色的那个准心然后让目标的健…

阿里云 - MaxCompute研究

一、官方介绍MaxCompute是适用于数据分析场景的企业级SaaS&#xff08;Software as a Service&#xff09;模式云数据仓库&#xff0c;提供离线和流式数据的接入&#xff0c;支持大规模数据计算及查询加速能力。MaxCompute适用于100 GB以上规模的存储及计算需求&#xff0c;最大…

全国青少年软件编程(Scratch)等级考试一级考试真题2022年12月——持续更新.....

1.小明想在开始表演之前向大家问好并做自我介绍,应运行下列哪个程序?( ) A. B. C. D. 正确答案:D 答案解析: 外观积木配合显示时间,才能看清楚内容。 2.舞台有两个不同的背景,小猫角色的哪个积木能够切换舞台背景?( ) A.<

UVC静态杀菌模组的工作原理及应用

现代紫外线消毒技术是基于现代防疫学、光学、生物学和物理化学的基础上&#xff0c;利用特殊设计的高效率&#xff0c;高强度和长寿命的C波段紫外光发生装置&#xff0c;产生的强紫外C光照射空气或物体表面&#xff0c;当空气或固体表面中的各种细菌、病毒、寄生虫、水藻以及其…

C/C++ 三维数组和二维数组指针的结合

示例程序&#xff1a;#include <iostream> #include <stdio.h> int main() {int a[3][4] {{1,2,3,4},{2,3,4,5},{3,4,5,6}};int b[3][4] {{10,11,12,13},{11,12,13,14},{12,13,14,15}};int(*aa[2])[4] { a,b };int* p1[3] {a[0],a[1],a[2]};int* p2[3] {b[0],…