当前位置: 首页 > article >正文

旅行商问题(TSP)的 C++ 动态规划解法教学攻略

一、问题描述

旅行商问题(TSP)是一个经典的组合优化问题。给定一个无向图,图中的顶点表示城市,边表示两个城市之间的路径,边的权重表示路径的距离。一个售货员需要从驻地出发,经过所有城市后回到驻地,要求总的路程最短。

二、输入输出形式

输入形式

输入的第一行包含两个整数 nm,分别表示顶点个数和边数。接下来的 m 行中,每行包含三个整数 uvw,表示顶点 u 和顶点 v 之间有一条边,边的权重为 w

输出形式

输出一个整数,表示最短路程的总长度。

三、样例输入输出

样例输入

5 10
1 2 3
1 3 1
1 4 5
1 5 8
2 3 6
2 4 7
2 5 9
3 4 4
3 5 2
4 5 3

样例输出

16

四、算法分析与设计

1. 动态规划算法

旅行商问题可以通过动态规划(DP)来解决。其核心思想是利用状态压缩来记录已经访问过的城市集合,并使用动态规划表来存储到达每个城市的最短路径。

2. 状态表示

定义 dp[S][i] 表示已经访问了集合 S 中的城市,最后到达城市 i 的最短路径。其中,S 是一个位掩码,表示已经访问过的城市集合。例如,S = (1 << n) - 1 表示所有城市都被访问过。

3. 状态转移

对于每个状态 S,遍历所有可能的城市 i,如果 i 在集合 S 中,则尝试将未访问的城市 j 加入集合 S,更新新的状态 S | (1 << j) 的最短路径。

4. 初始状态和结果提取

初始状态为 dp[full][i] = dist[i][0],其中 full 表示所有城市都被访问过,dist[i][0] 是城市 i 回到起点 0 的距离。最终结果从 dp[1][0] 中提取,表示从起点 0 出发,访问所有城市后回到起点的最短路径。

五、代码实现

#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;const int INF = 0x3f3f3f3f;int main() {int n, m;cin >> n >> m;// 初始化邻接矩阵vector<vector<int>> dist(n, vector<int>(n, INF));for (int i = 0; i < n; i++) {dist[i][i] = 0;}// 输入边信息并构建邻接矩阵for (int i = 0; i < m; i++) {int u, v, w;cin >> u >> v >> w;u--; // 转换为从 0 开始的索引v--;dist[u][v] = w;dist[v][u] = w;}// 动态规划表int full = (1 << n) - 1;vector<vector<int>> dp(1 << n, vector<int>(n, INF));// 初始化动态规划表,full 表示所有城市都被访问过for (int i = 0; i < n; i++) {dp[full][i] = dist[i][0];}// 填充动态规划表for (int S = full; S >= 0; S--) {if ((S & 1) == 0) continue; // 必须包含起点(城市 0)for (int i = 0; i < n; i++) {if (!((S >> i) & 1)) continue; // 城市 i 不在集合 S 中时跳过if (S == full) {continue; // full 已经初始化}for (int j = 0; j < n; j++) {if ((S >> j) & 1) continue; // 城市 j 已经在集合 S 中时跳过dp[S][i] = min(dp[S][i], dp[S | (1 << j)][j] + dist[i][j]);}}}// 输出结果cout << dp[1][0] << endl;return 0;
}

六、代码解析

1. 邻接矩阵初始化

首先,我们创建一个大小为 n x n 的邻接矩阵 dist,初始化所有元素为 INF(表示无穷大)。然后将对角线元素设置为 0,因为每个城市到自身的距离为 0。接下来,读取边信息并更新邻接矩阵。

2. 动态规划表初始化

定义动态规划表 dp,其中 dp[S][i] 表示已经访问了集合 S 中的城市,最后到达城市 i 的最短路径。初始状态为 dp[full][i] = dist[i][0],其中 full 表示所有城市都被访问过,此时需要回到起点 0

3. 状态转移填充

full 开始,逐步减少集合的大小,填充动态规划表。对于每个状态 S 和城市 i,如果 i 在集合 S 中,则尝试将未访问的城市 j 加入集合 S,更新新的状态 S | (1 << j) 的最短路径。

4. 输出结果

最终结果从 dp[1][0] 中提取,表示从起点 0 出发,访问所有城市后回到起点的最短路径。

七、复杂度分析

1. 时间复杂度

动态规划的状态数为 O(2^n * n),每个状态需要遍历所有可能的城市 n,因此总时间复杂度为 O(2^n * n^2)

2. 空间复杂度

动态规划表的空间复杂度为 O(2^n * n),用于存储每个状态和城市的最短路径。

八、总结

旅行商问题是一个典型的组合优化问题,通过动态规划算法可以有效地解决。该算法利用状态压缩和动态规划表,能够在合理的时间内找到最短路径。对于较小规模的问题,这种方法是可行的,但对于大规模问题,可能需要更高效的算法或启发式方法。

希望本攻略能够帮助你理解旅行商问题的动态规划解法,并能够在实际问题中应用。如果有任何疑问或需要进一步的解释,请随时提问!

http://www.lryc.cn/news/2404653.html

相关文章:

  • unix/linux,sudo,其内部结构机制
  • Hadoop 3.x 伪分布式 8088端口无法访问问题处理
  • Redis线程安全深度解析:单线程模型的并发智慧
  • 零基础在实践中学习网络安全-皮卡丘靶场(第十期-Over Permission 模块)
  • 北京大学肖臻老师《区块链技术与应用》公开课:12-BTC-比特币的匿名性
  • [Harmony]网络状态监听
  • 毕设 基于机器视觉的驾驶疲劳检测系统(源码+论文)
  • Ubuntu18.6 学习QT问题记录以及虚拟机安装Ubuntu后的设置
  • Vue3中computed和watch的区别
  • 发版前后的调试对照实践:用 WebDebugX 与多工具构建上线验证闭环
  • 瀚文(HelloWord)智能键盘项目深度剖析:从0到1的全流程解读
  • Shell编程核心符号与格式化操作详解
  • 针对“仅某个地区出现Bug”的原因分析与解决方案
  • 学习STC51单片机30(芯片为STC89C52RCRC)
  • sql中group by使用场景
  • 将HTML内容转换为Canvas图像,主流方法有效防止文本复制
  • Python-进程
  • Paraformer分角色语音识别-中文-通用 FunASR demo测试与训练
  • 【从0-1的CSS】第1篇:CSS简介,选择器以及常用样式
  • 对抗反爬机制的分布式爬虫自适应策略:基于强化学习的攻防博弈建模
  • JDK21深度解密 Day 15:JDK21实战最佳实践总结
  • 手写muduo网络库(一):项目构建和时间戳、日志库
  • 每日算法刷题Day25 6.7:leetcode二分答案3道题,用时1h40min(遇到两道动态规划和贪心时间较长)
  • 14-Oracle 23ai Vector Search 向量索引和混合索引-实操
  • kubeadm安装k8s
  • 服务器新建用户无法使用conda
  • Web前端基础:JavaScript
  • 基于对比学习的带钢表面缺陷分类研究,整合SimCLR自监督预训练与YOLOv8目标检测框架的技术解析及Python实现方案
  • 基于AWS Serverless架构:零运维构建自动化SEO内容生成系统
  • 【.net core】天地图坐标转换为高德地图坐标(WGS84 坐标转 GCJ02 坐标)