旅行商问题(枚举,回溯,动态规划,贪心,分支界限)

文章目录

  • 问题描述
  • 暴力枚举
  • 回溯法
  • 动态规划法
  • 贪心法
  • 分支界限法

问题描述

假设有一个货郎担要拜访n个城市,他必须选择所要走的路程,路程的限制时每个城市只能拜访一次,而且最后要走到原来出发的城市,要求路径长度。

在这里插入图片描述

旅行商问题将要走过的城市建立成一个完全图。稠密图,所以用临接矩阵来存。
由于路径的特殊性,可以正走也可以反着走,所以一般存在两条最优路径同时也可以用这条性质检验算法的正确性。

暴力枚举

使用dfs枚举每一个点, 不适用剪枝的话就是暴力枚举方法

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>using namespace std;const int N = 10;int g[N][N], n, m;
int cv = 0, bv = 0x3f3f3f3f;
bool st[N];vector<int> ans, x;void dfs(int k)
{if (k == n){// printf("before cv : %d\n", cv);// printf("last : {%d, %d} = %d\n", 1, x[k - 1], g[1][x[k - 1]]);cv += g[1][x[k - 1]];x.push_back(x[0]);for (auto i : x)printf("%d ", i);puts("");printf("{cv : %d}\n", cv);if(cv < bv){bv = cv;ans = x;}cv -= g[1][x[k - 1]];//注意最后一个加的cv要减掉x.pop_back();//同样也要删掉return;}for (int i = 1; i <= n; i ++){if (!st[i]){st[i] = true;x.push_back(i);//注意x的添加要在前面不然后面下标会出错cv += g[x[k - 1]][i];// printf("{%d, %d} : %d\n", x[k - 1], i,  g[x[k - 1]][i]);dfs(k + 1);cv -= g[x[k - 1]][i];x.pop_back();st[i] = false;}}
}void out()
{puts("路径为:");for (int i = 0; i <= n; i ++){printf("%d", ans[i]);printf(i == n ? "\n" : "->");}
}int main()
{memset(g, 0x3f, sizeof g);scanf("%d%d", &n, &m);for (int i = 0; i < m; i ++){int a, b, c;scanf("%d%d%d", &a, &b, &c);g[a][b] = g[b][a] = min(g[a][b], c);}for (int i = 0; i <= n; i ++) g[i][i] = 0; st[1] = true; x.push_back(1);dfs (1);puts("最短路径为:");printf("%d\n", bv);out();reverse(ans.begin(), ans.end());out();puts("");return 0;
}

在这里插入图片描述

回溯法

回溯法就是在暴力枚举的是后加上剪枝函数减少枚举的结点数目
剪枝函数为
左剪枝:
当 c v > b v 时减去 当cv > bv时减去 cv>bv时减去
在这里插入图片描述
在暴力枚举的基础上加上这个剪枝函数就行

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>using namespace std;const int N = 10;int g[N][N], n, m;
int cv = 0, bv = 0x3f3f3f3f;
bool st[N];vector<int> ans, x;void dfs(int k)
{if (k == n){// printf("before cv : %d\n", cv);// printf("last : {%d, %d} = %d\n", 1, x[k - 1], g[1][x[k - 1]]);cv += g[1][x[k - 1]];x.push_back(x[0]);for (auto i : x)printf("%d ", i);puts("");printf("{cv : %d}\n", cv);if(cv < bv){bv = cv;ans = x;}cv -= g[1][x[k - 1]];//注意最后一个加的cv要减掉x.pop_back();//同样也要删掉return;}for (int i = 1; i <= n; i ++){if (!st[i]){st[i] = true;x.push_back(i);cv += g[x[k - 1]][i];// printf("{%d, %d} : %d\n", x[k - 1], i,  g[x[k - 1]][i]);if (cv <= bv)dfs(k + 1);cv -= g[x[k - 1]][i];x.pop_back();st[i] = false;}}
}void out()
{puts("路径为:");for (int i = 0; i <= n; i ++){printf("%d", ans[i]);printf(i == n ? "\n" : "->");}
}int main()
{memset(g, 0x3f, sizeof g);scanf("%d%d", &n, &m);for (int i = 0; i < m; i ++){int a, b, c;scanf("%d%d%d", &a, &b, &c);g[a][b] = g[b][a] = min(g[a][b], c);}for (int i = 0; i <= n; i ++) g[i][i] = 0; st[1] = true; x.push_back(1);dfs (1);puts("最短路径为:");printf("%d\n", bv);out();reverse(ans.begin(), ans.end());out();puts("");return 0;
}

搜索的结点数变成了
在这里插入图片描述
相比穷举减少了搜索的结点数

动态规划法

状态压缩dp
利用一个int位中的32位0/1bit码来表示图走了哪些点,如果此位为1表示经过,0表示还未经过

类似题目AcWing 91. 最短Hamilton路径

在这里插入图片描述

/*由于要遍历每一个点所以不能用最短路径算法从一个已知点到另一个点只需要关注两个状态:1、终点是什么, 2、经过了哪些点而dp[i][j] 表示从0到终点j,经过了二进制状态(每个点有走过和没走两个状态)的点的路径状态计算:dp[i][j] <- dp[i - j][k](在已经经过的点中,去掉点j的方案取最小)
*/
#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;const int N = 21, M = 1 << N;
int dp[M][N];
int w[N][N];
int n;int main()
{scanf("%d", &n);for (int i = 0; i < n; i ++)for (int j = 0; j < n; j ++)scanf("%d", &w[i][j]);memset(dp, 0x3f, sizeof dp);dp[1][0] = 0;for (int i = 0; i < 1 << n; i ++)for (int j = 0; j < n; j ++)if (i >> j & 1)for (int k = 0; k < n; k ++ )if (i >> k & 1)dp[i][j] = min(dp[i][j], dp[i - (1 << j)][k] + w[j][k]);// 转移到达第i个点时将第i个点的状态要去掉就// 例如要将100101的第2个点去掉就需要 - 000100 = 100001printf("%d\n", dp[(1 << n) - 1][n - 1]);// 100000 - 000001 = 0111111 要将n - 1位全置位为1只需要用n为1后面为0减个位1即可return 0;
}

贪心法

贪心就是从起点开始每次走从这个点出发权重最小的边
但是这个寻找局部最优解的过程找到的并不是全局最优解
思路和生成最小生成树的思路一样,由于是完全图稠密图,所以使用prim算法更好

#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;const int N = 510, INF = 0x3f3f3f3f;int g[N][N], dist[N];
bool st[N];
int n, m;
vector<int> ans;int prime()
{memset(dist, 0x3f, sizeof dist);int res = 0;// 最小生成树中的权重之和for (int i = 0; i < n; i ++){int t = -1;// t表示集合外与集合相连的边最小的结点for (int j = 1; j <= n; j ++)if (!st[j] && (t == -1 || dist[j] < dist[t]))// 集合外的,第一次直接赋值,值更小的t = j;st[t] = true;// 加入集合ans.push_back(t);if (i && dist[t] == INF) return INF;// 不是第一个节点且到集合的距离无穷,说明各个结点都不连通if (i) res += dist[t];for (int j = 1; j <= n; j ++)dist[j] = min (dist[j], g[t][j]);// 更新与集合相连的最小值}return res;
}void out()
{puts("路径:");for (int i = 0; i <= n; i ++){printf("%d", ans[i]);printf(i == n ? "\n" : "->");}
}int main()
{cin >> n >> m;memset(g, 0x3f, sizeof g);for (int i = 0; i < m; i ++){int a, b, c;scanf("%d%d%d", &a, &b, &c);g[a][b] = g[b][a] = min (g[a][b], c);// 无向图要将两个方向的边都赋上权重}int res = prime();if (res == INF) puts("impossible");else printf("%d\n", res + g[ans[n - 1]][1]);ans.push_back(ans[0]);out();reverse(ans.begin(), ans.end());out();return 0;
}

在这里插入图片描述

分支界限法

使用优先队列形式
cc为当前代价,rc为剩余结点的最小出边代价和
下界函数为: cc + rc
左剪枝:
当 c c > b c 时剪枝 当cc > bc时剪枝 cc>bc时剪枝
右剪枝:
当 c c + r c > b c 是剪枝 当cc + rc > bc是剪枝 cc+rc>bc是剪枝

归结左右剪枝都可以用bound = cc + rc进行剪枝

剩余结点最小出边代价和就是枚举剩余每条结点对没给结点只算最小的出边

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>using namespace std;const int N = 16;
const int INF = 0x3f3f3f3f;int g[N][N], n, m, bc = INF;
vector<int> ans;struct Node {int idx, cost, bound;vector<int> path;bool st[N];bool operator<(const Node& other) const {return bound + cost > other.bound + other.cost;  // 按照 bound + cost 降序排序}
};int bound(const Node& x) {int minCost = 0;for (int i = 1; i <= n; ++i) {if (x.st[i]) {int m = INF;for (int j = 1; j <= n; ++j) {if (x.st[j]) {m = min(m, g[i][j]);}}minCost += m;}}return minCost;
}void bfs() {priority_queue<Node> heap;Node head = {1, 0, 0, {1}, {false}};head.st[1] = true;heap.push(head);while (heap.size()) {auto t = heap.top();heap.pop();if (t.idx == n) {int cc = t.cost + g[t.path[t.idx - 1]][1];for (auto i : t.path)printf("%d ", i);printf("%d", 1);puts("");if (cc < bc){bc = cc;ans = t.path;}continue;}for (int i = 1; i <= n; ++i) {if (!t.st[i]) {Node newNode = t;newNode.st[i] = true;newNode.path.push_back(i);newNode.cost += g[newNode.path[newNode.idx - 1]][i];newNode.idx++;newNode.bound = bound(newNode); if(newNode.bound < bc)//左右剪枝通用,因为是排列树左右都要算下界函数heap.push(newNode);}}}
}void out()
{puts("路径:");for (int i = 0; i <= n; i ++){printf("%d", ans[i]);printf(i == n ? "\n" : "->");}
}int main() {memset(g, 0x3f, sizeof g);scanf("%d%d", &n, &m);for (int i = 0; i < m; ++i) {int a, b, c;scanf("%d%d%d", &a, &b, &c);g[a][b] = g[b][a] = min(g[a][b], c);}bfs();printf("%d\n", bc);ans.push_back(ans[0]);out();reverse(ans.begin(), ans.end());out();return 0;
}

在这里插入图片描述

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

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

相关文章

为销售赋能:利用 Splashtop 增强远程培训技术

远程销售团队这一概念在当今快节奏的商业环境中日益普遍。各公司正在计划在不同地点灵活开展销售业务&#xff0c;希望利用技术优势缩小地域差距。但是&#xff0c;这种向远程销售的转型面临着重大挑战&#xff0c;尤其在培训和发展领域。培训远程销售团队需要采用创新方法&…

常见树种(贵州省):012茶、花椒、八角、肉桂、杜仲、厚朴、枸杞、忍冬

摘要&#xff1a;本专栏树种介绍图片来源于PPBC中国植物图像库&#xff08;下附网址&#xff09;&#xff0c;本文整理仅做交流学习使用&#xff0c;同时便于查找&#xff0c;如有侵权请联系删除。 图片网址&#xff1a;PPBC中国植物图像库——最大的植物分类图片库 一、茶 灌…

鸿蒙 ark ui 轮播图实现教程

前言&#xff1a; 各位同学有段时间没有见面 因为一直很忙所以就没有去更新博客。最近有在学习这个鸿蒙的ark ui开发 因为鸿蒙不是发布了一个鸿蒙next的测试版本 明年会启动纯血鸿蒙应用 所以我就想提前给大家写一些博客文章 效果图 具体实现 我们在鸿蒙的ark ui 里面列表使…

土地利用数据技术服务

一、背景介绍 土地是人类赖以生存与发展的重要资源和物质保障&#xff0c;在“人口&#xff0d;资源&#xff0d;环境&#xff0d;发展&#xff08;PRED&#xff09;”复合系统 中&#xff0c;土地资源处于基础地位。随着现代社会人口的不断增长以及工业化、城市化进程的加速&a…

Excel使用VLOOKUP查询数据

VLOOKUP函数在百度百科中的解释是&#xff1a; 解释一下&#xff0c;函数需要4个参数&#xff1a; 参数1&#xff08;lookup_value&#xff09;&#xff1a;需要匹配的值参数2&#xff08;table_array&#xff09;&#xff1a;在哪个区域里进行匹配参数3&#xff08;col_index…

Dubbo3使用Zookeeper作为注册中心的方案讨论!详解DubboAdmin与PrettyZoo来监控服务的优劣!

文章目录 一&#xff1a;Dubbo注册中心的基本使用 二&#xff1a;Zookeeper注册中心的使用 1&#xff1a;依赖引入 2&#xff1a;实际开发 三&#xff1a;Zookeeper作为注册中心的使用展示 1&#xff1a;启动注册Zookeeper服务 2&#xff1a;引入注册中心 (一)&#xf…

Java 21增强对Emoji表情符号的处理了

现一个 Java 21 中有意思的东西&#xff01; 在java.Lang.Character类中增加了用于确定字符是否为 Emoji 表情符号的 API&#xff0c;主要包含下面六个新的静态方法&#xff1a; public static boolean isEmoji(int codePoint) {return CharacterData.of(codePoint).isEmoji(…

操作系统 day13(RR、优先级调度)

RR&#xff08;时间片轮转&#xff09; 响应时间&#xff1a;系统中有10个进程正在并发执行&#xff0c;如果时间片为1秒&#xff0c;则一个进程被响应可能需要等待9秒。也就是说&#xff0c;如果用户在自己进程的时间片外通过键盘发出调试命令&#xff0c;可能需要等待9秒才能…

中断方式的数据接收

中断接收简介 回顾之前的代码 之前的代码是 等待标志位RXNE位为1才有数据 进而读取数据存放在变量c中 再根据c变量的数据是为0还是为1进而编写灯亮灭的代码 if语句 但这样的代码明显不符合裸机多任务的编程模型 因为在while中为进程 进程执行的时间不能大于5ms 但是while&…

Qt/QML编程学习之心得:一个Qt工程的学习笔记(九)

1、.pro文件 加CONFIG += c++11,才可以使用Lamda表达式(一般用于connect的内嵌槽函数) 2、QWidget 这是Qt新增加的一个类,基类,窗口类,QMainWindow和QDialog都继承与它。 3、Main函数 QApplication a应用程序对象,有且仅有一个 a.exec() 进行消息循环、阻塞 MyWi…

《图解Java数据结构与算法:微课视频版》简介

本书系统、全面地介绍数据结构的基础理论与算法设计&#xff0c;精选数据结构考研习题和各类典型例题进行讲解&#xff0c;案例和课后习题丰富&#xff0c;突出对数据结构算法实践能力的培养。本书算法均采用Java语言实现&#xff0c;示例代码可直接上机运行。 本书配套资源丰…

Spring-jdbcTemplate-配置数据库连接池,配置文件方式beans.xml

1、jdbc.properties jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql:///studb jdbc.userroot jdbc.pwd123456 2、beans.xml <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans&…

Python BDD 框架比较之 pytest-bdd vs behave

pytest-bdd和behave是 Python 的两个流行的 BDD 测试框架&#xff0c;两者都可以用来编写用户故事和可执行的测试用例&#xff0c; 具体选择哪一个则需要根据实际的项目状况来看。 先简单看一下两者的功能&#xff1a; pytest-bdd 基于pytest测试框架&#xff0c;可以与pytest…

港口大型设备状态监测及预测性维护策略

在现代港口运营中&#xff0c;大型设备的正常运行对于保障港口作业的高效性至关重要。为了实现设备的可靠性和持续性&#xff0c;港口管理者需要采取一系列状态监测和预测性维护策略。 推进自动化和智能化是提高港口大型设备状态监测和维护管理效率的重要途径。通过应用先进的…

【计算机网络笔记】数据链路层概述

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

读像火箭科学家一样思考笔记07_探月思维

1. 挑战“不可能”的科学与企业 1.1. 互联网 1.1.1. 和电网一样具有革命性&#xff0c;一旦你插上电源&#xff0c;就能让自己的生活充满活力 1.1.2. 互联网的接入可以帮助人们摆脱贫困&#xff0c;拯救生命 1.1.3. 互联网还可以提供与天气相关的信息 1.2. 用廉价、可靠的…

Windows如何截取屏幕图片以及动态图

在制作PPT或是其他演示文稿或是说明文档的时候&#xff0c; 常常需要截取网页或是屏幕的截图&#xff0c;在Windows中有多种方式可以实现截取屏幕。 Windows 截取屏幕图片的方式 在Windows 中截取屏幕中某个区块的方式有&#xff1a; 方式1. 最原始的方式&#xff1a; 点击 …

C练习题_2

一、单项选择题(本大题共20小题,每小题2分,共40分。在每小题给出的四个备选项中选出一个正确的答案&#xff0c;并将所选项前的字母填写在答题纸的相应位置上。&#xff09; 以下叙述中错误的是&#xff08;) A.对于double类型数组&#xff0c;不可以直接用数组名对数组进行整…

机器学习与药物筛选的心得体会

机器学习在药物设计里面的应用可以说还是比较常见的&#xff0c;尤其是搞计算的都会或多或少的涉及到这块。比如国内做这块比较多的&#xff0c;浙江大学的侯廷军教授&#xff0c;北京化工大学的闫爱霞教授&#xff0c;华东理工大学的几个做模拟计算的老师&#xff0c;上海药物…

Unity机器学习 ML-Agents第一个例子

上一节我们安装了机器学习mlagents的开发环境&#xff0c;本节我们创建第一个例子&#xff0c;了解什么是机器学习。 我们的例子很简单&#xff0c;就是让机器人自主移动到目标位置&#xff0c;不能移动到地板范围外。 首先我们来简单的了解以下机器学习的过程。 机器学习的过…