这个问题可以堪称一个全排列,[起点,剩下的全排列]

回溯法

import numpy as npclass backtracking_Traveling_saleman:# 初始化,明确起点def __init__(self,graph,start=0):# 顶点的个数self.vertex_num = len(graph)self.graph = graphself.start = start# 当前解,当前解为顶点的集合[起点,顶点1,...顶点n-1],初始时,第0个为起点# 从第一个到n-1个为排列树# 解为[起点,顶点1,...顶点n-1]# 解的目标函数为:起点到顶点1距离(depth =1)+顶点1到顶点2的+...+顶点depth-1到depth的距离# 顶点n-2到顶点n-1(depth=n-1)距离,最后还要构成环路+顶点n-1到起点的距离,求上述和的最小值。self.curSolution = [i for i in range(self.vertex_num)]# 用于存最好的解self.bestSolution = [-1] *self.vertex_num# 当前花费初始距离为0self.curCost = 0# 界初始为很大,得到一个解之后更新,也可以提前更新self.bestCost = np.infdef backtracking(self,depth):# 当到达最后一个顶点,为递归出口if depth == self.vertex_num-1:# 最后一层时,目前函数应该为:当前的距离和+前一个顶点到最后顶点距离+最后顶点到起点的距离temp_cost = self.curCost + self.graph[self.curSolution[depth-1]][self.curSolution[depth]] + self.graph[self.curSolution[depth]][self.curSolution[self.start]]# 当前解优于最优解的话,更新最优解,假如求可行解的话,就需要把可行解保存起来if temp_cost < self.bestCost:self.bestCost = temp_costself.bestSolution[:] = self.curSolution[:]else:# 下面就是排列树的处理,我们需要求除了起点之外的[顶点1,...顶点n-1]n-1个起点的全排列#  我们处理的是标号,也就是self.curSolution = [i for i in range(self.vertex_num)]# 所以我们全排列都是self.curSolution里面的数for i in range(depth,self.vertex_num):
#                self.curSolution[depth],self.curSolution[i] = self.curSolution[i],self.curSolution[depth]# 当满足我们定义的界时,才可以进入下一层,也就是走过的长度<最优值的话(注意我们这儿要求的是最小值),我们才继续搜索,否则剪掉# 有没有人注意到这里是depth-1到i,而不是depth-1 到 depth?# 实际上我们学习到模板应该是,先交换,然后满足constraint() && bound() 回溯,最后再交换# 编码时先交换的话,这个地方就应该写成depth-1 到 depth,还没交换的话就是depth-1到i# 这里是做了优化处理,不满足条件的话,交换都没有必要执行if self.curCost + self.graph[self.curSolution[depth-1]][self.curSolution[i]] < self.bestCost:# 全排列,把 当前 和第一位交换,除第一位以外剩下的的进行全排列self.curSolution[depth],self.curSolution[i] = self.curSolution[i],self.curSolution[depth]# 同理这里为什么是depth-1 到 depth,为不是depth-1到i,也是一样的道理self.curCost += self.graph[self.curSolution[depth-1]][self.curSolution[depth]]# 进入下一层self.backtracking(depth+1)# 回溯处理,恢复现场self.curCost -= self.graph[self.curSolution[depth-1]][self.curSolution[depth]]self.curSolution[depth],self.curSolution[i] = self.curSolution[i],self.curSolution[depth]
#                self.curSolution[depth],self.curSolution[i] = self.curSolution[i],self.curSolution[depth]def print_Result(self):# 我们需要求除了起点之外的[顶点1,...顶点n-1]n-1个起点的全排列self.backtracking(1)print(self.bestCost)   print(self.bestSolution)

限界分支法,优先级队列实现方式

约束条件:只能未走过的点
代价函数:走过的长度+后续未走过的长度

如下的优先级队列方式实现,当前最后解一直到底部才更新,没有提前更新,所以首先有一次走到底部,而越靠近root的结点的界是越小的,因为大部分都是最小出边和,那么走到底之后,就会回到靠近靠近root的结点再次往下走,加入当前最优解较大的话,那他就一直处于优先级队列的尾部,无出头之日,那优先级队列就相当于深度优先回溯了。

import numpy as np
import heapq
class Node:def __init__(self,curCost=None,depth=None,rcost = None,path = None,lbound =None):# 限界函数,我们定义限界函数为:当前走过的长度+未走过的结点最小出边的长度和# 这个用于优先级队列排序self.lbound = lbound# 当前走过的距离self.curCost = curCost# 当前的解的深度self.depth = depth# 路径,从0到depth为已走过的,depth+1到n-1为未走过的结点self.path = path# depth到n-1为未走过的结点的最小出边的和self.rcost = rcost# 这个用于结点比较大小,之前的(weights,node)方式有时会出问题,是有时候,# 现在使用这种方式,lt means less thandef __lt__(self,other):return int(self.lbound) <int(other.lbound)class prune_Traveling_saleman:def __init__(self,graph,start=0):self.num = len(graph)self.graph =  graphself.start = start# 用于存储最优的结果self.bestNode = Node(np.inf,-1,-1,None,-1)# 用于存储每个顶点的最小出边self.minOut = [np.inf]*self.num# 所有的最小出边之和self.minSum =0for i in range(self.num):for j in range(self.num):if i !=j and graph[i][j] < self.minOut[i]:self.minOut[i] = graph[i][j]self.minSum += self.minOut[i]def traveling_Saleman(self):pqueue =[]# 第0层,就是起点,也可以认为是第一层,这里为了处理数据方便成为第0层# [i for i in range(self.num)]为开始的路径[0,1,2,3],path[0]是已经确定为0# 最开始curCost =0,depth=0,rcost =self.minSum,lbound=  self.minSumcurNode = Node(0,0,self.minSum,[i for i in range(self.num)],self.minSum)# 把第一个结点放入pqueue = [curNode,]depth =0while depth <= self.num-2:# 弹出结点curNode = heapq.heappop(pqueue)curCost = curNode.curCostdepth = curNode.depthrcost = curNode.rcostpath = curNode.path# 当处于倒数第2层时,就可以处理最后结果了,可以考虑结束了if depth == self.num-2:# 处于当处于倒数第2层时,lbound就是当前走过的距离+当前结点到最后一个结点的距离# + 最后一个结点到起点的距离;实际上也可以考虑self.num-1层,那就只需要加上# 当前结点到起点的距离lbound = curCost + self.graph[path[depth]][path[depth+1]] + self.graph[path[depth+1]][path[0]]# 如果下界小于当前最优的结果,就可以考虑输出结果了if lbound < self.bestNode.curCost:# 把当前值和当前路径输出self.bestNode.curCost = lboundself.bestNode.path = path# 以下只是方便退出,当depth == self.num-1时退出node = Node(lbound,depth+1,lbound,path,lbound)heapq.heappush(pqueue,node)else:# 一般情况下首先更新下一层,也就是未走过结点最小出边和,# 需要减去当前结点的最小出边,temp_rcost = rcost - self.minOut[path[depth]]for i  in range(depth+1,self.num):# 当前走过的距离,需要更新为加上当前结点到下一个结点的距离temp_cur = curCost + self.graph[path[depth]][path[i]]# 当前结点的下界为,当前走过的距离+未走过结点最小出边和lbound =  temp_cur + temp_rcost# 只有当下界小于self.bestNode.curCost才有放入优先级队列的必要if lbound < self.bestNode.curCost:#  # 放入前需要更新path里面这一层depth加入的结点号# 只要把path[depth]记录为当前选择的结点就行了,同时path[depth]位置# 之前放置的那个结点放到刚刚空的那个地方就行了# 这一层还有其他的分支,所有不能直接在path上操作,因为这样下一个分支也# 用这个path就乱了。也可以可以先换了,压入之后再换回来temp_path = path[:]temp_path[depth+1],temp_path[i] = temp_path[i],temp_path[depth+1]# 把这个分支压入优先级队列node = Node(temp_cur,depth+1,temp_rcost,temp_path,lbound)heapq.heappush(pqueue,node)def print_Result(self):self.traveling_Saleman()print(self.bestNode.curCost)print(self.bestNode.path)

测试结果

g =[[0,5,9,4],[5,0,13,2],[9,13,0,7],[4,2,7,0]]backtracking_Traveling_saleman(g,0).print_Result()
prune_Traveling_saleman(g,0).print_Result()23
[0, 1, 3, 2]
23
[0, 1, 3, 2]

实际上也可以考虑self.num-1层,那就只需要加上当前结点到起点的距离

    def traveling_Saleman(self):pqueue =[]# 第1层,就是起点,也可以认为是第一层,这里为了处理数据方便成为第1层curNode = Node(0,0,self.minSum,[i for i in range(self.num)],self.minSum)pqueue = [curNode,]curNode = heapq.heappop(pqueue)curCost = curNode.curCostdepth = curNode.depthrcost = curNode.rcostpath = curNode.pathwhile depth <= self.num-1:# 处于解的最后一层,lbound就为当前的距离+当前点到起点的距离# 在这种情况下需要注意,需要把结点弹出放在循环体的最后,到达self.num就# 退出循环了,不会再进入判断体,否者进入判断,else就会报越界if depth == self.num-1:lbound = curCost + self.graph[path[depth]][path[0]]if lbound < self.bestNode.curCost:self.bestNode.curCost = lboundself.bestNode.path = pathnode = Node(lbound,depth+1,lbound,path,lbound)heapq.heappush(pqueue,node)else:   temp_rcost = rcost - self.minOut[path[depth]]for i  in range(depth+1,self.num):temp_cur = curCost + self.graph[path[depth]][path[i]]lbound =  temp_cur + temp_rcostif lbound < self.bestNode.curCost:#  should fixtemp_path = path[:]temp_path[depth+1],temp_path[i] = temp_path[i],temp_path[depth+1]node = Node(temp_cur,depth+1,temp_rcost,temp_path,lbound)heapq.heappush(pqueue,node)curNode = heapq.heappop(pqueue)curCost = curNode.curCostdepth = curNode.depthrcost = curNode.rcostpath = curNode.pathdef print_Result(self):self.traveling_Saleman()print(self.bestNode.curCost)print(self.bestNode.path)

货郎问题:回溯法和限界分支法相关推荐

  1. 01背包问题:回溯法和限界分支、递归和迭代方式

    01背包问题 递归方式模板: void backtrack(int t){ if(t > n) output(x); else{ for(int i = f(n,t); i <= g(n, ...

  2. 限界分支法(队列方式)追踪解:01背包问题

    追踪解 追踪解,上述实现的情况下,解都在最后一层,根本不知道之前的路径是怎样的,广度优先搜索,同一个纬度,假如不加指标判断的话,根本不知道最优解是选择的哪一个,所以需要同一个纬度的每一个结点,记住他之 ...

  3. 限界分支法(实际上没有剪枝,介绍的是广度优先搜索):01背包问题,队列实现方式(FIFO)

    限界分支法:队列实现方式 前面已经介绍过限界分支法大部分是基于广度优先搜索,广度优先搜索一般借助于队列实现,剪枝的情况可以借助于优先级队列. 实现如下: #%% class FIFO_01_Pack: ...

  4. 限界分支法优先级队列方式出口和追踪解的两种方法总结

    在优先级队列分支限界法法中,何时为出接口,也就是while循环何时退出了? 解空间为[0,1,2-,n-1],当depth = n-1时,就可以记录结果了,可以考虑循环体退出了(实际上能不能出,还要看 ...

  5. 限界分支法:01背包问题,优先级队列(包含解的追踪)

    前面提到: 不知道大家注意到没有?上述实现方式没有使用单位体积价值的排序,和之前提到01背包回溯法基于单位体积价值实现不一样(先装单位体积价值高的). 我们网上经常看到都是基于以上实现的,到底这个用有 ...

  6. 回溯法和分支限界法解决旅行商问题

    实验三 旅行商问题 一. 实验内容 二.实验目的 三. 算法描述 1.回溯算法描述: 2.分支限界法算法描述: 四. 算法实现 1.数据结构及函数说明 (1) 回溯法求解TSP问题 (2) 分支界限求 ...

  7. C语言的双向链表头插法和尾插法,指定节点删除

    文章目录 前言 头插法 尾插法 删除节点 测试代码如下 前言 双向链表和单链表的唯一区别就是多个一个指针域而已,该指针域可以访问链表的上一个节点. 关于构造双向链表的过程我们常见的有两种方法,和单链表 ...

  8. 骆驼命名法,帕斯卡命名法和匈牙利命名法(转)

    一.匈牙利命名法:广泛应用于象Microsoft Windows这样的环境中.       Windows 编程中用到的变量(还包括宏)的命名规则匈牙利命名法,这种命名技术是由一位能干的 Micros ...

  9. 计算机软件技术 上海电力学院,上海电力学院 计算机软件技术 实验三 用头插法和尾插法创建线性表...

    上海电力学院计算机软件技术实验三用头插法和尾插法创建线性表 #include #define MAXLEN 9 struct table {int key; int othererm; } ; typ ...

最新文章

  1. 强烈推荐几个好玩的深度学习github项目分享!
  2. 简单的一点总结:关于优惠券功能
  3. Divide and conquer:K Best(POJ 3111)
  4. Colors on the web
  5. SAP 销售云支持的丰富的报表显示类型
  6. 【Oracle】lsnrctl reload 命令简介
  7. Using #region Directive With JavaScript Files in Visual Studio 【转载】
  8. python 评分卡
  9. java获取汉字首字母
  10. 数字图像处理·SLIC超像素分割算法C++实现
  11. Navicat 设置自动插入时间触发器
  12. 【产品设计】用户画像模板
  13. 科学型IT人才和工程型IT人才
  14. 过孔为什么不能打焊盘上?我就想打,怎么办?
  15. Android 常用 Manager的总结
  16. 矢量计算机和标量计算机
  17. 输入被除数和除数,求商。
  18. C++(三十六)之电视机和遥控器友元类
  19. Jmeter接口测试——配置全局token
  20. 22.1.30总结反思

热门文章

  1. 7000p壁纸怎么换_这些圣诞壁纸,劝你们马上点开,保存!真的超好看
  2. linux下截取安卓手机屏幕,截取屏幕截图
  3. 鸿蒙系统安装过程中出错,求助求助——鸿蒙系统Windows环境搭建时hpm安装失败!!!...
  4. ndows live id怎么登陆,手机如何注册和使用Windows Live ID帐号
  5. java代码的运行顺序_java中的代码块执行顺序
  6. fastjson字段改名/设置别名
  7. 【thymeleaf】th:with
  8. 【maven插件】maven-resources-plugin
  9. 【mysql】时间戳
  10. 二维分类教案_大班数学二维分类教案