旅行商问题【状压dp】
一。问题介绍
旅行商问题,即TSP问题(Travelling Salesman Problem)又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求的路径路程为所有路径之中的最小值。
二。解决方法(理论)
TSP是一道经典的NP-完全问题。在规模比较小的时候,可以用动态规划求解。
有n个城市,两两之间均有道路直接相连,给出每个城市i和j之间的道路长度dist(i, j)。求一条经过每个城市一次且仅一次,最后回到起点的路线,使得经过的道路总长度最短。n<=16,城市编号为0~n-1。
因为路线是一个环,可以规定任意点为起点和终点,不妨设城市0为起点和终点。设d(s,i)表示“当前在i城市,已访问城市集合为s”的最短长度,则d(s, i)=min{d(s - i, j) + dist(j, i) | i∈S}.
初始时d({0},0)=0,最终答案为min{d({0,1,2,,,,n-1}, i) + dist(i, 0) | i≠0}
时间复杂度为O()。
根据该思路可以写下如下代码:
memset(dp, 0x3f, sizeof(dp));
dp[1][0] = 0;
for (int s = 0; s < (1<<n); s++) { //枚举集合状态sfor (int i = 0; i < n; i++) { //枚举当前的点iif (s & (1<<i)) {for (int j = 0; j < n; j++) { //枚举之前的点jif (j != i && (s & (1<<j)) {dp[s][i] = min(dp[s][i], dp[s ^ (1<<i)][j] + dist[j][i]); //根据之前的点j,更新当前的点i。}}}}
}
int ans = INF:
for (int i = 1; i < n; i++) { //注意这里的i是从1开始的,因为访问城市的时候我们规定从0号城市访问,要走一个回路需要走n步,第n-1步必须是非0号城市,所以不能包括0。ans = min(ans, dp[(1<<n) - 1][i] + dist[i][0]);
}
cout << ans << endl;
【Note】
- 上面的dp[s ^ (1<<j)][i]中的异或^表示集合s减去第i个元素。
- 主要步骤是3步,西安枚举集合状态,再枚举当前点i,再枚举之前的点j。(前面的例题是正着推,前面的状态推后面所有的状态。本题的TSP是反着推,后面状态由所有前面的状态推出。)
三。实现代码步骤
1.先定义好用到的数组,定义一个dp[s][i]来表示“当前在城市i,已访问城市集合为s”的最短路径。在int dist[20][20];下面继续写下
int dp[1<<16][20]; //第一维表示已经走过的城市集合s, 第二维表示当前在哪个城市
2. 若考虑初始状态,起点是0,所以dp[1][0]=0,其他状态置为无穷大。
memset(dp, 0x3f, sizeof(dp));
dp[1][0] = 0; //因为起点是0,所以当前访问的城市为0,第二维是0,将第0个城市加入到集合中,二进制表示为1,所以dp[1][0] = 0。
3. 从小到大遍历集合S,枚举当前所在的城市i,城市i必须在集合S中,即必须满足( s & (1<<i)) != 0.
【Note】枚举每个城市,也就是求dp[当前集合,第i个城市],求当前走到第i个城市的,且走过的城市集合相同的路径最小值。
for (int s = 0; s < (1<<n); s++) {for (int i = 0; i < n; i++) {if (s & (1<<i)) {}}
}
4. 枚举上一个城市j,j也必须在集合S中,所以必须满足(s & (1<<j) != 0.转移方程为d(s, i) = min{d{s-{i} + dist(j, i) | i ∈ S}。
for (int j = 0; j < n; j++) {if (j !=i && (s & (1<<j)) { //第j个城市与第i个城市不相同,且第j个城市也在走过的集合S内。dp[s][i] = min(dp[s][i], dp[s ^ 1 << i][j] + dist[j][i]);}
}
【Note】这里巧妙地用异或^来表示集合的减法,如果集合B是集合A的子集,那么集合A异或集合B的结果就是A-B。如集合A为10100(二进制),集合B为100(二进制),则集合A^集合B = 10000(二进制),相当于集合A-集合B。当然dp[s ^ 1<<i][j] 也可以用dp[s-(1<<j)][j]表示,用异或主要是因为位运算比加减乘除运算快的多。
5. 最后我们枚举集合包含所有元素的状态,也即集合为{0, 1, 2.....n-1),并且返回起点0,然后找到最小值就是我们需要的答案,即mid(d({0, 1, 2,,,,n-1}, i) + dist(i, 0) | i ≠ 0}
int ans = INF;
for (int i = 1; i < n; i++) { //注意,这里序号从1开始,因为我们假设回路是从0访问的,走到最后一个城市后,要返回第0个城市,所以不能包含第0个城市。ans = min(ans, dp[(1<<n) - 1][i] + dist[i][0];
}
if (ans == INF) ans = -1; //如果找不到相应的路径,就返回-1
cout << ans << endl;
6.测试一下:
/* Input:
3
-1 1 10
1 -1 2
10 2 -1Output:
13*/
【Note】到了这里,程序基本就结束了,但是如果我们改一下TSP问题,去掉每个城市只能摆放一次的限制。这样一来,从城市i到城市j只需走最短路即可,那么我们要用Floyd算法求出每两个城市之间的最短路。则在main函数的memset(dp, 0x3f, sizeof(dp));前面写下:
for (int k = 0; k < n; k++) {for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);}}
}
四。整体代码;
1.未修改的TSP问题代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;int INF = 0x3f3f3f3f;
int dist[20][20];
int dp[1<<16][20];
int main() {int n;cin >> n;for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {cin >> dist[i][j];if (dist[i][j] == -1) dist[i][j] = INF;}}memset(dp, 0x3f, sizeof(dp));dp[1][0] = 0;for (int s = 0; s < (1<<n); s++) {for (int i = 0; i < n; i++) {if (s & (1<<i)) {for (int j = 0; j < n; j++) {if (j != i && (s & (1<<j))) {dp[s][i] = min(dp[s][i], dp[s ^ 1 << i][j] + dist[j][i]);}}}}}int ans = INF;for (int i = 1; i < n; i++) {ans = min(ans, dp[(1<<n) - 1][i] + dist[i][0]);}if (ans == INF) ans = -1;cout << ans << endl;return 0;
}
结果如下图所示。 结果为13.
2.修改条件后的TSP代码(也即取消一个城市只能拜访一次的条件)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;int INF = 0x3f3f3f3f;
int dist[20][20];
int dp[1<<16][20];
int main() {int n;cin >> n;for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {cin >> dist[i][j];if (dist[i][j] == -1) dist[i][j] = INF;}}memset(dp, 0x3f, sizeof(dp));//update Floyd Algorithmfor (int k = 0; k < n; k++) {for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);}}}dp[1][0] = 0;for (int s = 0; s < (1<<n); s++) {for (int i = 0; i < n; i++) {if (s & (1<<i)) {for (int j = 0; j < n; j++) {if (j != i && (s & (1<<j))) {dp[s][i] = min(dp[s][i], dp[s ^ 1 << i][j] + dist[j][i]);}}}}}int ans = INF;for (int i = 1; i < n; i++) {ans = min(ans, dp[(1<<n) - 1][i] + dist[i][0]);}if (ans == INF) ans = -1;cout << ans << endl;return 0;
}
结果如下,结果为6.
旅行商问题【状压dp】相关推荐
- 【动态规划】状压dp:蓝桥2020补给(旅行商问题)
问题可以转换为: 从0出发途径每个城市至少一次返回0求最短路径: 解法:动态规划(状压dp)+Floyd预处理最短路径(从一个城市到另一个城市走最短路即可,不需考虑途径什么城市) [动态规划笔记]状压 ...
- HDU-3001(旅行商问题+三进制状压dp)
#include <bits/stdc++.h> using namespace std; /* 题目大意: 给定n个点m条边的带权无向图,问有没有可能从n个点的其中一个出发,走遍其余n ...
- ACM-ICPC 2018 南京赛区网络预赛 E AC Challenge 状压DP
题目链接: https://nanti.jisuanke.com/t/30994 Dlsj is competing in a contest with n (0 < n \le 20)n(0& ...
- 状压dp个人刷题记录
目录 一.普通型 蒙德里安的梦想 题意: 思路: code: #2153. 「SCOI2005」互不侵犯 题意: 思路: code: P1879 [USACO06NOV]Corn Fields G 题 ...
- CSU1129 送货到家 状压dp
哈哈发现这道题竟然没有题解,于是我决定写一份! 状压dp 题目: 懒惰的巫女Reimu因为各种原因在香霖堂的店主Rinnosuke那儿欠下了很多债,于是乎只好靠帮他在幻想乡中送货来偿还掉微不足道的一小 ...
- POJ 1038 Bugs Integrated Inc (复杂的状压DP)
\(POJ~1038~~*Bugs~Integrated~Inc:\) (复杂的状压DP) \(solution:\) 很纠结的一道题目,写了大半天,就想练练手,结果这手生的.其实根据之前那道炮兵阵地 ...
- codeforces 8C. Looking for Order 状压dp
题目链接 给n个物品的坐标, 和一个包裹的位置, 包裹不能移动. 每次最多可以拿两个物品, 然后将它们放到包里, 求将所有物品放到包里所需走的最小路程. 直接状压dp就好了. #include < ...
- UVA10296 Jogging Trails(中国邮递员问题)(欧拉回路、一般图最大权匹配 / 状压DP)
整理的算法模板合集: ACM模板 目录 思路 UVA10296 Jogging Trails 题目翻译: 给你n个点,m条无向边,每条边有一定的距离数值,构造成一个连通图.问从任意一点出发,遍历所有的 ...
- POJ 2411 Mondriaan‘s Dream(最清楚好懂的状压DP讲解)(连通性状态压缩DP)
poj 2411 Mondriaan's Dream(最清晰的状压DP解析) 闫氏DP大法好 我们这里是一列一列地来,因为是一个棋盘性的状态压缩DP,从哪个方向都一样 摆放的小方格总方案数 等价于 横 ...
- 【每日DP】day2、P1879 [USACO06NOV]Corn Fields G玉米地(状压DP模板题)难度⭐⭐⭐★
昨天的每日DP我还在写01背包,今天就到状压DP了,真刺激. P1879 [USACO06NOV]Corn Fields G 题目链接 输入 2 3 1 1 1 0 1 0 输出 9 一道简单的状压D ...
最新文章
- 【6】font-size 字体属性
- sudo 命令表示 Linux sudo命令以系统管理者的身份执行指令,也就是说,经由 sudo 所执行的指令就好像是 root 亲自执行。 使用权限:在 /etc/sudoers 中有出现的使用
- 前端开发js运算符单竖杠“|”的用法和作用及js数据处理
- java 多线程 异步日志_精彩技巧(1)-- 异步打印日志的一点事
- Spring-JDBC通用Dao
- CF1146F - Leaf Partition(树形dp)
- graphql入门_GraphQL入门指南
- 前端学习(2680):注意看位置 少加注释
- error U1087: cannot have : and :: dependents for same target
- 每日英语:Lighting: Twigs Shine in Home Decor
- linux的系统移植——交叉编译工具集
- python box2d模拟平抛运动_论述如何基于Box2D模拟星球重力效果
- Java:URLEncoder、URLDecoder、Base64编码与解码
- mysql查询结果进行排名
- asp车辆租赁-汽车租赁管理系统
- 2018tfe世界计算机专业排名,2018年TFE TIMES美国研究生计算机科学专业排名
- 如何编写爬虫获取淘宝网上所有的商品分类以及关键属性 销售属性 非关键属性数据
- 目标检测一阶段和二阶段对比图
- reads去污染接头
- Deep Blind Video Super-resolution
热门文章
- java文件放在哪里_Java文件路径
- rmd中无法打开链结r_从零开始入门R语言—Rstudio下载与安装
- 【愚公系列】2021年12月 python爬虫自动化-爬虫环境搭建
- matlab mbd 淘宝,完美起航-基于模型(MBD)的树莓派程序开发——设置树莓派自动连接wifi和使用指令连接Matlab/Simulink和树莓派(不使用树莓派连接向导连接)...
- 北滘职业技术学校计算机,北窖职业技术学校
- Html网页按住Ctrl+滚轮缩放后,盒子大小改变的罪魁祸首竟是border?(已解决)
- MOS管工作原理,就是这么简单
- 华为无线-AC+AP小型无线网络配置实验_v1
- 小白机器学习基础算法学习必经之路
- 记 今日头条广告架构社招面试