zoukankan      html  css  js  c++  java
  • 北航算法作业三

    题意

    有50个城市,知道各个城市之间的距离(一个50*50的矩阵,有向图),知道各条路径上的过路费(一个50*50的矩阵,有向图).问在过路费不超过1500的情况下,从0号城市走到49号城市的最短路径.要求用分支限界法

    解题思路

    分支限界法的作用是剪枝,它终究只是搜索过程中的一种剪枝优化策略.所以,分支限界既能用于广度优先也能用于深度优先还能用于启发式搜索等一切搜索技术.
    分支限界法关键在于一个上界,一个下界.
    上界的意思是完成任务花费的最大值,也就是从出发点到当前状态已经花费了多少.如果已经花费了很多,那么从当前状态再往下走已经没有意义了,应该放弃前行,因为已经离开出发点很远了.就像蜜蜂寻找花蜜,如果飞离巢穴很远了还没有找到蜂蜜,那就不再向远处飞了,而要回头还在附近找.
    下界的意思是完成任务花费的最小值,也就是从当前状态到目标状态至少要花费多少.这个下界是需要估计的,它是展望一下将来还要付出多少代价.如果当前状态一抬头,看见目标状态还有那么那么远,而自己现在已经快没油了.那么现在怎么办?一种方案是继续前行,直到把油耗尽也到达不了目的地,那岂不是白搭,还不如知难而退尽早收手.所以上策是回头找.
    上界一般很好找到,难找的是下界.上界尽量小,下界尽量大,那么剪枝就会快.也就是把当前状态的取值范围压缩在很小的一个区间内,剪枝效率就会高.
    在本题中,路费的上界是1500,是一个固定值,距离的上界需要一开始给定一个初值(可取9999),在搜索过程中逐渐变小.距离和花费的下界需要使用Floyd算法预处理出来,实际上要求的就是每一个结点到目标结点的最短距离和路费.
    于是有人说:既然都是到一个目标结点,那何不用dijstra求目标结点到其它结点的最短路径.答曰:不可以,这是有向图,从目标节点出发进行dijstra求出来的结果是目标结点到其它结点的最短距离和最小花费.于是又说:那把边存储的时候反向一下不可以吗?答曰:不可以,比如2->3这条边如何反向存储,如果存储成3->2,那么目标结点可能无法到达3了.综上可知,dijstra只适用于有向图单源到多点最短路径,也适用于无向图多点到单源和单源到多点最短路径.有向图的多点到单源最短路径只能用Floyd算法来处理.
    上面用Floyd求完之后,只是获得了在不考虑路费的情况下的最短路径,以及不考虑距离的情况下的最小花费.而这两者是有关联的,这种关联带来的后果就是:一旦将两者综合考虑,距离和花费都无法到达最小值.这有点像"柯西洗袜子不等式":f(x)+f(y)>f(x+y).
    Folyd求完下界之后,就可以施展搜索技术了.理论上,深搜广搜都可以.实际上,广搜会爆队列.除非把初始的距离上界设置的比较小(如500),那么可以秒秒钟求出答案.
    下面重点讨论一下广搜中的几个坑.

    如上图所示,(x,y)表示(dis,cost)即该条边的距离和花费.不能因为访问过2,就不允许再访问2了.第一次访问2虽然距离比较近,但是它的花费太大了.第二次访问2虽然距离远但是花费小.也就是说,在广度优先中不能通过"已访问"来剪枝.

    深度优先可以递归实现(区区50,不会爆栈),也可以自定义一个栈来实现.

    代码

    import queue
    
    
    class Node:
       def __init__(self, city_number, now_cost, now_dis, path):
          self.city, self.cost, self.dis, self.path = city_number, now_cost, now_dis, path
    
       def __lt__(self, other):
          return self.dis < other.dis
    
    
    def matrixFromFile(fileName):
       return [[int(value) for value in line.split()] for line in open(fileName, "r")]
    
    
    def floyd(g):
       a = [[g[i][j] for j in range(N)] for i in range(N)]
       for i in range(N): a[i][i] = 0
       for i in range(N):
          for j in range(N):
             for k in range(N):
                a[j][k] = min(a[j][k], a[j][i] + a[i][k])
       return a
    
    
    def bfs():
       q = queue.PriorityQueue()
       q.put(Node(0, 0, 0, "0"))
       # 只有一开始的这个上界不知道怎么求,如果设置的太大,效率太低
       min_node = Node(0, 0, 500, "no answer")
       while q.qsize():
          now = q.get()
          print(now.path, now.dis, q.qsize())
          if now.dis > min_node.dis: break
          for i in range(N):
             if now.path.find(str(i)) != -1: continue
             next = Node(i, now.cost + cost[now.city][i], now.dis + dis[now.city][i], now.path + "->" + str(i))
             if next.cost + floyd_cost[i][aim] > cost_bound or next.dis + floyd_dis[i][aim] > min_node.dis: continue
             if next.city == aim:
                min_node = next
                continue
             q.put(next)
       return min_node
    
    
    def dfs(now, now_dis, now_cost, vis, path):
       global min_dis
       if now == aim:
          min_dis = now_dis
          print(path, "dis:{} cost:{}".format(now_dis, now_cost))
       vis[now] = True
       for i in range(N):
          if vis[i]: continue
          if now_dis + dis[now][i] + floyd_dis[i][aim] > min_dis or now_cost + cost[now][i] + floyd_cost[i][
             aim] > cost_bound: continue
          dfs(i, now_dis + dis[now][i], now_cost + cost[now][i], vis, path + "->" + str(i))
       vis[now] = False
    
    
    dis = matrixFromFile("m1.txt")
    cost = matrixFromFile("m2.txt")
    N = len(dis)
    floyd_dis = floyd(dis)
    floyd_cost = floyd(cost)
    aim = N - 1
    cost_bound = 1500
    # 深度优先
    min_dis = 9999
    dfs(0, 0, 0, [False] * N, "0")
    # 广度优先
    # ans=bfs()
    # print(ans.path, "dis={} cost={}".format(ans.dis, ans.cost))
    
  • 相关阅读:
    鸿蒙的js开发模式19:鸿蒙手机下载python服务器端文件的实现
    【资源下载】Linux下的Hi3861一站式鸿蒙开发烧录(附工具)
    并发编程大扫盲:带你了解何为线程上下文切换
    内存溢出 MAT 排查工具,它真香香香
    面试官问:如何排除GC引起的CPU飙高?我脱口而出5个步骤
    小学妹问我:如何利用可视化工具排查问题?
    你不得不知的6个JDK自带JVM调优工具
    那个小白还没搞懂内存溢出,只能用案例说给他听了
    听说同学你搞不懂Spring Boot集成Mybatis的玩法,田哥来教你
    手把手教你设置JVM调优参数
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/6144622.html
Copyright © 2011-2022 走看看