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

状压 dp --- TSP 问题

大家好,今天是 2025 年 9 月 5 日,我们继续开启状压 dp 的学习。

TSP 问题的题目问法和解决方案相对固定。

一:旅行商问题简介:

旅行商问题(Travelling salesman problem,TSP),是这样的一个问题:

给定 n 个城市以及每对城市之间的距离(完全图),求解访问每座城市一次且仅一次并回到起始位置城市的最短回路(哈密顿回路)(哈密顿图)。旅行商问题是一个 NP 完全问题,到目前为止没有多项式时间的高效算法。(不存在O(n),O(n * n),O(n * n * n)……等高效算法)。

看到这个问题之后,我们首先肯定可以想到一个暴力解法:

我们可以暴力枚举 1~n 的全排列,根据每一种排列方式求出每一种路线的一个最短路。

但是,不幸的是,这样实现的话时间复杂度为 O(n * n!)n > 10 就会超时。

我们接下来的状压 dp 解法可以将时间复杂度优化到 O(n * n * 2^n)但是实际运行的时间是要远远小于这个数的,原因就是:我们在枚举状态的时候会有很多剪枝。因此 n > 20 才有可能超时。

二:旅行商问题模板题

题目一:售货员的难题

题目链接:售货员的难题

【题目描述】

【算法原理】

通过读题,我们不难发现,这道题目就是给我们一个完全图,然后让我们求最短哈密顿回路。

扫一眼数据范围,显然我们可以尝试使用状压 dp 来解决这道问题。

我们首先先来暴力枚举一些路径,找一找规律:

我们发现,如果我们不考虑最后一步的话,其实就时从0号点出发,把所有的点都走一遍,此时的一个最短路。

1.状态表示

我们可以这样定义状态表示:

f[st][i]:表示从起点出发,每个点只能走一次,走过的点的状态为 st 时,最终落在 i 号点时的一个最短路。

2.最终结果

如果我们这样定义状态表示的话,结合题目要求,最终结果显然如下:

3.状态转移方程

推导状态转移方程时,我们要根据最近的一步来考虑问题:

假设我们现在在 i 号点位置,我们要枚举的是上一个点 j 的位置:

但是我们要保证走到 i 号点的路径中要包含 j 这个点:

那么我们就可以先走到 j 号点,再从 j 号点走向 i 号点。

4.初始化

因为我们要求的是最短路,因此所有的格子初始化为正无穷。

但是我们要从0号点开始出发,因此 f[1][0] = 0;

5.填表顺序

我们要从小到大枚举所有的状态,至于倒数第一个点和倒数第二个点的枚举顺序没有先后要求。

因为 st 中去除掉一个二进制中的 1 后,一定是小于 st 的。

【代码实现】

版本一:先枚举最后一个点,再枚举前面一个点

#include <iostream>
#include <cstring>using namespace std;const int N = 21;int n;
int e[N][N];int f[1 << N][N];int main()
{cin >> n;for(int i = 0; i < n; i++)for(int j = 0; j < n; j++)cin >> e[i][j];memset(f, 0x3f, sizeof f);f[1][0] = 0;for(int st = 2; st < (1 << n); st++)for(int i = 0; i < n; i++) // 枚举倒数第一个点 if((st >> i) & 1)for(int j = 0; j < n; j++) // 枚举倒数第二个点if((st >> j) & 1)f[st][i] = min(f[st][i], f[st ^ (1 << i)][j] + e[j][i]);int ret = 0x3f3f3f3f;for(int i = 1; i < n; i++) ret = min(ret, f[(1 << n) - 1][i] + e[i][0]);cout << ret << endl; return 0;
}

版本二:先枚举前面一个点,再枚举最后一个点

#include <iostream>
#include <cstring>using namespace std;const int N = 21;int n;
int e[N][N];int f[1 << N][N];int main()
{cin >> n;for(int i = 0; i < n; i++)for(int j = 0; j < n; j++)cin >> e[i][j];memset(f, 0x3f, sizeof f);f[1][0] = 0;for(int st = 2; st < (1 << n); st++)for(int j = 0; j < n; j++) // 枚举倒数第二个点if((st >> j) & 1)for(int i = 0; i < n; i++) // 枚举倒数第一个点 if((st >> i) & 1)f[st][i] = min(f[st][i], f[st ^ (1 << i)][j] + e[j][i]);int ret = 0x3f3f3f3f;for(int i = 1; i < n; i++) ret = min(ret, f[(1 << n) - 1][i] + e[i][0]);cout << ret << endl; return 0;
}

最后解释一个问题:上述代码中,是如何保证所有的点仅仅只走一次的???

题目二:最短 Hamilton 路径

题目链接:最短 Hamilton 路径

本题是一个二倍经验的题目,题目和上一题几乎一模一样,因此这里只给出代码实现。

【代码实现】

#include <iostream>
#include <cstring>using namespace std;const int N = 21;int n;
int e[N][N];
int f[1 << N][N];int main()
{cin >> n;for(int i = 0; i < n; i++)for(int j = 0; j < n; j++)cin >> e[i][j];memset(f, 0x3f, sizeof f);f[1][0] = 0;for(int st = 2; st < (1 << n); st++)for(int i = 0; i < n; i++)if((st >> i) & 1)for(int j = 0; j < n; j++)if((st >> j) & 1)f[st][i] = min(f[st][i], f[st ^ (1 << i)][j] + e[j][i]);cout << f[(1 << n) - 1][n - 1] << endl;return 0;
} 

好的,今天的分享就到这里了。谢谢大家!!!


文章转载自:

http://Dksmsy5q.wkgyz.cn
http://hHwpAM9N.wkgyz.cn
http://PSuAUol9.wkgyz.cn
http://dVJcsFt0.wkgyz.cn
http://pcNVAae2.wkgyz.cn
http://EPvxkdhJ.wkgyz.cn
http://kzDBWwgC.wkgyz.cn
http://uCav4tiL.wkgyz.cn
http://zFRZNrnR.wkgyz.cn
http://SFFkqMh0.wkgyz.cn
http://BgGdjMGC.wkgyz.cn
http://cEy4jotm.wkgyz.cn
http://rtRBfIJF.wkgyz.cn
http://Nw16lGnX.wkgyz.cn
http://1sdD7nyT.wkgyz.cn
http://XEIzcUrl.wkgyz.cn
http://lGqc29Ek.wkgyz.cn
http://CtvAnlmg.wkgyz.cn
http://ss6Wf8TX.wkgyz.cn
http://9m8dOAk9.wkgyz.cn
http://aGKcJiGD.wkgyz.cn
http://b6Dwynpr.wkgyz.cn
http://WqeMo80b.wkgyz.cn
http://6OjF7YWx.wkgyz.cn
http://CBsCMAhc.wkgyz.cn
http://qDZStPB8.wkgyz.cn
http://Y6y8vNm5.wkgyz.cn
http://uphnVNXw.wkgyz.cn
http://1ffnAs2d.wkgyz.cn
http://cIb8FHZL.wkgyz.cn
http://www.dtcms.com/a/369683.html

相关文章:

  • 【数字孪生核心技术】什么是倾斜摄影?
  • 公共卫浴感应开关选红外还是雷达
  • 解决 Apache/WAF SSL 证书链不完整导致的 PKIX path building failed 问题
  • 计算机二级C语言操作题(填空、修改、设计题)——真题库(17)附解析答案
  • 上位机通信基础知识
  • Acrobat-2025.001.20643_Win中文_PDF编辑器_便携版安装教程
  • Java基础 9.5
  • javafx笔记
  • 大基座模型与 Scaling Law:AI 时代的逻辑与困境
  • 扩展与改进的密钥协商协议
  • Spring整合MQTT使用
  • AI应用开发-技术架构 PAFR介绍
  • 9月5日星期五今日早报简报微语报早读
  • Zynq-7000 上 RT-Thread 的 MMU 与 SMP 优势分析
  • 【完整源码+数据集+部署教程】西兰花实例分割系统源码和数据集:改进yolo11-AggregatedAtt
  • 数据库查询优化
  • PiscCode基于 Mediapipe 实现轨迹跟踪
  • 硬件(三) 通信方式、串口通信
  • 在 CentOS 上完整安装 Docker 指南
  • 详解人造卫星遭遇的地球反射光与月球反射光
  • NAF、INRAS、NACF论文解读
  • 【Linux】系统部分——进程间通信1(管道)
  • 从策略到实效|Adobe Target 实战应用与成功案例
  • 连锁门店可用性监测和进程监测最佳实践
  • 残差网络ResNet
  • 人工智能之数学基础:逻辑回归算法的概率密度函数与分布函数
  • Pinia 两种写法全解析:Options Store vs Setup Store(含实践与场景对比)
  • MySQL抛出的Public Key Retrieval is not allowed
  • 贵州移动创维E900V22F-S905L3SB-全分区备份
  • HarmonyOSAI编程自然语言代码生成