小伙伴们,你们好。今天我来浅显的讲一下这个可重复访问城市的TSP问题,所谓的可重复就是城市和路线都随便走,只要最后它的路径总和是最小的就行。

要用到的知识点是 状态压缩dp 和 Floyd算法

一、Floyd算法

Floyd算法:floyd算法学习视频
这个小姐姐会用手算的方式带你了解floyd算法的整个过程,相信看完你就有一种恍然大悟的感觉了

我下面floyd算法的主要作用是让我们得到一个距离二维数组,路径二维数组

1.distance[][]

例子:distance[i][j] 表示点i到点j的最短距离
有了这个数组,我们第一步就完成了

2.path[][]

例子:path[i][j]表示点i到点j中间经过了什么点

 public void floyd(Double[][] distance,int[][] path,Double[][] edge) {int n = edge.length;//edge数组是一个邻接矩阵//初始化一下传进来距离数组for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {path[i][j] = -1;if (i!=j) {distance[i][j]  = edge[i][j];}else {distance[i][j] = 0.0;}}}//核心算法for (int k=0;k<n;k++) {for (int i=0;i<n;i++) {for (int j=0;j<n;j++) {if (distance[i][j]>distance[i][k]+distance[k][j]) {distance[i][j] = distance[i][k]+distance[k][j];path[i][j] = k;//记录路径的数组}}}}}

通过调用函数jointPath(…)我们就可以找到v0=>v的完整路径了,这对我们求TSP的路径也是至关重要的

//int[][] path:由floyd得到
//v0:起点,v:终点
//idx[0]:因为我要记住p数组的有效长度,所以用了idx[0]来保证它可以当指针用
//ps:我有点菜,写得不是很好public void jointPath(int[][] path,int v0,int v,int[] p,int[] idx) {getPath(path,v0,v,p,idx);p[idx[0]++] = v;}//好好用纸来模拟一下这个递归函数,你就悟了public void getPath(int[][] path,int v0,int v,int[] p,int[] idx) {if (path[v0][v]==-1) {p[idx[0]++] = v0;return;}getPath(path,v0,path[v0][v],p,idx);//左边getPath(path,path[v0][v],v,p,idx);//右边}

写完博客,我会录制视频在b站,你们可以关注一下我,看一下我的视频讲解 : 随风的叶子(我的账号名称)

二、状态压缩dp

视频学习连接,看前33分钟就行
这个视频里面,他会教你dp的概念还有怎么使用与、或、非和异或
这老师讲得还是十分详细的,里面讲解的题目:最短Hamilton路径
这道题和我们要解决的问题有着异曲同工之妙,能够解决上面的这道题,相信下面你们快速的掌握。

欢迎回来,相信你对dp有一定了解了.

下面,我们要有这样一种思维:就是我们只注重点i到点j的最短距离,就是我们把心放在distance[][]数组上面,我们无需关系i到j之间通过了什么点,所以我们dp集合加入了i和j,那么i和j之间包含1了什么点我们不管,也不会加入到dp集合中,我们关心的只有最短,因为点和边是可以重复的

你应该get到我的点了吧

接下来,我就和你讲讲这道题的具体思路:

首先我们开一个dp[1<<n][n]的数组

因为我们要表示每个集合的状态,而状态有0~111…111(2^n-1)

当我们成功的将我们的状态搞到111…111(2^n-1)就是我们胜利的时刻了

下面,我来上代码来说话(要认真看我的注释噢)

dp[s][i] = Math.min(dp[s][i],dp[s^(1<<i)][j]+distance[j][i]);//只是至关重要的,这玩意的作用就是假如还没有将顶点i加入集合中(实际我们已经加入了),通过存在集合当中的顶点j为踏板来到i的距离会不会比原来的距离要短

然后,我还是想和大家说一下
ps: 以下从第0位开始

  • s&(1<<i) 例子:假如s:1101(2进制),i=2(10进制),1<<i=100(2进制),这时1101&0100 = 0100,然后我们就可以通过判断s&(1<<i) 等不等于0来确定s(2进制)第i位是0还是1了
  • s^(1<<i) 例子,假如s:1101(2进制),i=2(10进制),1<<i=100(2进制),这时1101 ^ 0100 = 1001(我们就可以将s(2进制)的第i位变成0了)
 //==================状态压缩DPDouble[][] dp = new Double[1<<n][n];//开数组//初始化dp数组for (int i = 0; i < 1<<n; i++) {for (int j = 0; j < n; j++) {dp[i][j] = Double.MAX_VALUE;}}dp[1][0] = 0.0;//将0号点放进集合里面,0:表示当前在点0处停留for (int s=1;s<(1<<n);s++) {//1~11..11(2^n-1[1<<n])for (int i=0;i<n;i++) {if ((s&(1<<i))!=0) {//顶点i加入了集合当中for (int j = 0; j < n; j++) {if ((s&(1<<j))!=0&&i!=j) {dp[s][i] = Math.min(dp[s][i],dp[s^(1<<i)][j]+distance[j][i]);}}}}}Double ans = Double.MAX_VALUE;//初始化一下//最后,我们将所有的点都加入了集合当中,那么我们从最后加入的那个点回到原点就大公告成了,从一个点到另一个点的最短距离我们还是从distance[][]里面获取//也是这里启示我自己想出来了求路径的方法for (int i=1;i<n;i++) {//0是起始点,就不要加入里面了if (ans > dp[(1<<n)-1][i]+distance[i][0]) {ans = dp[(1<<n)-1][i]+distance[i][0];}}return n==1?0:ans;//健壮性

下面有必要声明一下:
@Autowired
private FloydUtil floydUtil;(这玩意是因为我写springboot项目用了,你们看到函数名知道我在放什么屁就行了,抱拳了)

  • public void TSP_can_repeat_path(Double[][] dp,Double[][] dis,int[] path,int end,int n)
  • public void completePath(int[][] path,int[] p,int[] initP,int[] idx)

ps:参数具体什么含义,看我注解就知道了

  • 我先说一下我第一个算法的作用,现有状态s就是不断的回退状态t,然后找到状态t到状态s的最短路径来确定(再重复一遍:我这里i->j,可能里面还包含了别的点,只是我们通过两点的最短路路径看成它们相通罢了)


看这张图:箭头右边的数组表示第i位变位0了,当最后集合变成0就是我们胜利的时刻了(重复一次:从第0位开始

if (index==-1) break;//结束条件,你也可以自己优化一下,反正我就爱这么写[哈哈],当下标没有变化的时候就结束了

这个时候,我们只是成功了一半,

0   1   2   3   13   14   15   12   4   11   16   11   5   6   5   10   17   18   9   19   8   7 0

再再再重复一遍:0=>1或者 10=>7,表示的只是最短路径,我们还要将他们中间存在的点找出来,这个时候的找我们就回到floyd算法里面涉及的找路径,我们通过一个for就能全部找出来了,

floydUtil.getPath(…):我这里这个函数不会将它最后一个元素加入数组中

认真看下面的代码就理解整个过程了,期待我在b站把视频肝出来吧

//求出路径/**** @param p  (完整路线数组)* @param iii (上面数组的有效下标)* @param n   (顶点个数)* @return   (返回路径最小值)*/public void TSP_can_repeat_path(Double[][] dp,Double[][] dis,int[] path,int end,int n) {//end从0开始int s = (1<<n)-1;//path数组的长度等于顶点的个数(11...111)int idx = 0;//初始化为0path[idx++] = end;//最后一个 去吧你//System.out.println(Integer.toBinaryString(s));//二进制,我要看看出现了什么幺蛾子while (true) {s = s^(1<<end);//新状态//System.out.println(Integer.toBinaryString(s)+"-->"+end);Double min = Double.MAX_VALUE;int index = -1;for (int i = 0; i < n; i++) {if ( (s&(1<<i))!=0 && min > dp[s][i]+dis[i][end]) {min = dp[s][i]+dis[i][end];index = i;}}if (index==-1) break;path[idx] = index;end = index;idx ++;}for (int i = 0; i < (n / 2) - 1; i++) {int tmp = path[i];path[i] = path[n-1-i];path[n-1-i] = tmp;}}/**** @param path (floyd之后的路径)* @param p    (要填补的数组)* @param initP (上一层搞的路径)* @param idx    (记录下标)*/public void completePath(int[][] path,int[] p,int[] initP,int[] idx) {int n = initP.length;for (int i = 0; i < n-1; i++) {int[] res =  new int[n];int[] index = new int[1];floydUtil.getPath(path,initP[i],initP[i+1],res,index);for (int j = 0; j < index[0]; j++) {p[idx[0]++] = res[j];}}p[idx[0]++] = initP[n-1];//最后一个点放进来}

下面是我写的完整代码

package com.lin.utils;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component
public class FloydDPUtil {@Autowiredprivate FloydUtil floydUtil;/**** @param p  (完整路线数组)* @param iii (上面数组的有效下标)* @param n   (顶点个数)* @return   (返回路径最小值)*/public Double TSP_can_repeat(int[] p,int[] iii,int n) {//==================floydDouble[][] distance = new Double[n][n];int[][] path = new int[n][n];floydUtil.floyd(distance,path);//==================状态压缩DPDouble[][] dp = new Double[1<<n][n];//初始化dp数组for (int i = 0; i < 1<<n; i++) {for (int j = 0; j < n; j++) {dp[i][j] = Double.MAX_VALUE;}}dp[1][0] = 0.0;//将0号点放进集合里面for (int s=1;s<(1<<n);s++) {for (int i=0;i<n;i++) {if ((s&(1<<i))!=0) {//就是顶点i加入了集合当中for (int j = 0; j < n; j++) {if ((s&(1<<j))!=0&&i!=j) {dp[s][i] = Math.min(dp[s][i],dp[s^(1<<i)][j]+distance[j][i]);}}}}}Double ans = Double.MAX_VALUE;int idx = -1;for (int i=1;i<n;i++) {//0是起始点,就不要加入里面了if (ans > dp[(1<<n)-1][i]+distance[i][0]) {ans = dp[(1<<n)-1][i]+distance[i][0];idx = i;}}//搞路径到数组initP里面int[] initP = new int[n+1];TSP_can_repeat_path(dp,distance,initP,idx,n);initP[n] = 0;//开始解析里面的东西,放到数组p中completePath(path,p,initP,iii);return ans;}//求出路径public void TSP_can_repeat_path(Double[][] dp,Double[][] dis,int[] path,int end,int n) {//end从0开始int s = (1<<n)-1;//path数组的长度等于顶点的个数(11...111)int idx = 0;//初始化为0path[idx++] = end;//最后一个 去吧你//System.out.println(Integer.toBinaryString(s));//二进制,我要看看出现了什么幺蛾子while (true) {s = s^(1<<end);//新状态//System.out.println(Integer.toBinaryString(s)+"-->"+end);Double min = Double.MAX_VALUE;int index = -1;for (int i = 0; i < n; i++) {if ( (s&(1<<i))!=0 && min > dp[s][i]+dis[i][end]) {min = dp[s][i]+dis[i][end];index = i;}}if (index==-1) break;path[idx] = index;end = index;idx ++;}for (int i = 0; i < (n / 2) - 1; i++) {int tmp = path[i];path[i] = path[n-1-i];path[n-1-i] = tmp;}}/**** @param path (floyd之后的路径)* @param p    (要填补的数组)* @param initP (上一层搞的路径)* @param idx    (记录下标)*/public void completePath(int[][] path,int[] p,int[] initP,int[] idx) {int n = initP.length;for (int i = 0; i < n-1; i++) {int[] res =  new int[n];int[] index = new int[1];floydUtil.getPath(path,initP[i],initP[i+1],res,index);for (int j = 0; j < index[0]; j++) {p[idx[0]++] = res[j];}}p[idx[0]++] = initP[n-1];//最后一个点放进来}}

作者:随风

有什么问题可以私聊我qq:2338244917

你们可以关注我的b站账号:随风的叶子 跳转=>随风的叶子

我写完已经凌晨两点半了,还请给我点个赞

csdn粉丝数快突破300了,谢谢你们的支持

浅谈可重复访问城市的TSP问题(最短距离 + 具体走法)相关推荐

  1. 浅谈单片机中变量访问的互斥

    本文以stm32为硬件平台,浅谈一下变量访问的互斥. 假设是裸板,主程序和中断服务程序都对某个变量进行修改,那么很可能造成数据的不一致.查了一下,主要有两种解决方法:第一种是关中断,在访问公共资源的时 ...

  2. python 字典键值重复_浅谈python字典多键值及重复键值的使用

    在python中使用字典,格式如下: dict={ key1:value1 , key2;value2 ...} 在实际访问字典值时的使用格式如下: dict[key] 多键值 字典的多键值形式如下: ...

  3. 浅谈城市大脑与智慧城市发展趋势

    摘要:本文从城市大脑的概念出发,概述了智慧城市的宏观发展目标和新型智慧城市建设的基本原则及发展需求.重点阐述了城市大脑建设内容.智慧城市顶层设计1533+N系统架构.城市KPI指标.五个运行管理服务平 ...

  4. 浅谈旅行商问题(TSP问题)

    以POJ3311为例 题目描述: The Pizazz Pizzeria prides itself in delivering pizzas to its customers as fast as ...

  5. 优化 | 浅谈旅行商(TSP)的七种整数规划模型

    作者:彭澜 纽约州立大学水牛城分校工业工程系在读博士生,研究方向:车辆路径规划问题(VRP) 旅行商问题(TSP)是运筹学领域最知名的问题之一.本文将从整数规划模型建模的角度,介绍七种不同的建模方式, ...

  6. 【浅谈电商】如何防止重复支付

    [浅谈电商]如何防止重复支付 一.前言 最近正在做电商相关的项目,整理一下解决方案并帮助自己巩固知识点,此方案是结合了目前的业务环境,若有更好的解决的方式很高兴与大家一起讨论. 二.支付流程 要想知道 ...

  7. php字面量,浅谈js之字面量、对象字面量的访问、关键字in的用法

    一:字面量含义 字面量表示如何表达这个值,一般除去表达式,给变量赋值时,等号右边都可以认为是字面量. 字面量分为字符串字面量(string literal ).数组字面量(array literal) ...

  8. 浅谈文化创意产业与创意城市 | 上海城市规划

    本文通过对创意产业.文化产业.文化创意产业.创意城市等相关概念的梳理,揭示了文化创意产业的特征,并探讨了城市规划及城市公共政策对文化创意产业的作用.在理论综述的基础上,梳理并分析了以市场因素为主导的温 ...

  9. 浅谈网站访问速度优化

    周末女朋友公司的智慧医保项目上线了,但是web端访问速度比较慢,然后就来问问我有没有好的优化方案.于是就这篇[浅谈网站访问速度优化]就诞生了. 1.备案:好多个人网站为了方便,往往不喜欢备案,就把网站 ...

最新文章

  1. 打造全球最大规模 Kafka 集群,Uber 的多区域灾备实践
  2. python编写通讯录管理系统_一个简单的python程序实例(通讯录)
  3. 调试linux内核前的多虚拟机网络配置(图文教程)
  4. 【干货】产品经理必读:app开发版本迭代的节奏该如何把握?
  5. excel查标准正态分布_用EXCEL简易制作正态分布图
  6. wxWidgets:wxPGMultiButton类用法
  7. Boost::context模块callcc的jump_void测试程序
  8. http有哪些请求方法
  9. python算法题_python基本算法题(一)
  10. fermat数推素数无穷_如何在3分钟内对Fermat测试进行素数测试
  11. sql两个in并列_SQL窗口函数
  12. openjdk替换java_ubuntu中将java环境由安装版的openjdk替换为Oracle的jdk
  13. vue openlayer单击地图事件循环多次执行_Vue中$nextTick的理解
  14. Java IO(input output)流二
  15. Microsoft JScript提示‘DIRECT’未定义(2014-08-26记)
  16. 质量小的夸克之间,如何互换质量大的胶子
  17. python哈夫曼编码注意_[Python]哈夫曼编码
  18. 【桧木】桧木精油的功效 台湾桧木价值所在
  19. 原生Android应用接入flutter(详细步骤)
  20. 基于单幅图像Patch Map的稳健除雾(PMS-Net: Robust Haze Removal Based on Patch Map for Single Images_CVPR_2019)

热门文章

  1. arduino yun 京东_收购域名、注册商标,京东的“云布局”终于铺开了!
  2. MySql数据库——文件
  3. 100g的DDOS攻击大概是多少钱一天
  4. MAC报文格式的简单整理
  5. php超小免杀大马_php最新php大马免杀过狗最小1kB吗?
  6. Maven的pom文件
  7. 土豆网8月24起在纳市摘牌 CEO王微宣布退休
  8. 【ansys】ansys workbench 网格划分错误-无法生成边界层/膨胀层,生成边界层失败
  9. java代码连接打印机
  10. 如何使用DirectShow驱动H264/H265格式输出的UVC摄像头