Algorithm 在没有任何负前缀的图中寻找最短路径

Algorithm 在没有任何负前缀的图中寻找最短路径,algorithm,constraints,shortest-path,bellman-ford,Algorithm,Constraints,Shortest Path,Bellman Ford,在具有正边和负边的有向图中找到从源到目标的最短路径,这样在路径中的任何一点上,在它之前到达的边之和都不是负的。如果不存在这样的路径,也要报告 我试图使用改良的贝尔曼福特,但找不到正确的解决方案 我想澄清几点: 是的,可能存在负重量循环 n是边的数目 如果问题有解决方案,则假设存在O(n)长度的路径 +1/-1边缘权重 我会在这里使用递归暴力强制: 类似(伪代码以确保它不是特定于语言的) 您将需要: 二维布尔数组显示您可以去哪里和不能去哪里,这不应该包括“禁止值”,就像在负边缘之前,您可以选择

在具有正边和负边的有向图中找到从源到目标的最短路径,这样在路径中的任何一点上,在它之前到达的边之和都不是负的。如果不存在这样的路径,也要报告

我试图使用改良的贝尔曼福特,但找不到正确的解决方案



我想澄清几点:

  • 是的,可能存在负重量循环
  • n是边的数目
  • 如果问题有解决方案,则假设存在O(n)长度的路径
  • +1/-1边缘权重

  • 我会在这里使用递归暴力强制: 类似(伪代码以确保它不是特定于语言的)

    您将需要:

    • 二维布尔数组显示您可以去哪里和不能去哪里,这不应该包括“禁止值”,就像在负边缘之前,您可以选择添加垂直和水平“平移”,以确保它从[0][0]开始
    • 包含最短路径的整数(静态)
    • 由2个插槽组成的1D阵列,显示目标。[0]=x,[1]=y
    你将做:

    function(int xPosition, int yPosition, int steps)
    {
    if(You are at target AND steps < Shortest Path)
        Shortest Path = steps
    if(This Position is NOT legal)
        /*exit function*/
    else
        /*try to move in every legal DIRECTION, not caring whether the result is legal or not
        but always adding 1 to steps, like using:*/
        function(xPosition+1, yPosition, steps+1);
        function(xPosition-1, yPosition, steps+1);
        function(xPosition, yPosition+1, steps+1);
        function(xPosition, yPosition-1, steps+1);
    }
    
    函数(int-xPosition、int-yPosition、int-steps)
    {
    如果(您在目标位置,步数<最短路径)
    最短路径=步数
    如果(该职位不合法)
    /*退出功能*/
    其他的
    /*试着朝着每一个合法的方向前进,而不管结果是否合法
    但始终在步骤中添加1,如使用:*/
    函数(xPosition+1、yPosition、steps+1);
    函数(xPosition-1、yPosition、steps+1);
    函数(xPosition、yPosition+1、steps+1);
    函数(xPosition,yPosition-1,步骤+1);
    }
    
    那就用它运行吧 函数(StartingX,StartingY,0)


    最短路径将包含在静态外部int中。无可否认,这不是一个建设性的答案,但是它太长,无法在评论中发布

    在我看来,这个问题包含了二进制和离散背包问题,所以它最坏的运行时间充其量是。考虑一个连接和加权的图如下:

    然后,等价的二进制背包问题试图从∑ai最大化的集合{a0,…,an}中选择权重,其中∑ai 作为补充,如果我们引入加权循环,很容易构造无界背包问题


    <> P>因此,您可能选择的任何实际算法都有一个运行时间,这取决于您所考虑的“平均”情况。对于我没有考虑或没有掌握的问题,是否有限制?你似乎相当肯定这是一个O(n3)问题。(虽然在这种情况下n是什么?

    正如Kaganar所指出的,为了得到一个多时间算法,我们基本上必须做一些假设。假设边的长度为{-1,1}。给定该图,构造一个加权上下文无关语法,该语法识别从源到目标的有效路径,其权重等于多余1条边的数目(它概括了平衡括号的语法)。对于每个非终结符,计算最便宜的生产成本,方法是将所有内容初始化为无穷大或1,这取决于是否存在RHS没有非终结符的生产,然后将其放宽n-1倍,其中n是非终结符的数量。

    在一条注释中指出,该问题作为特例包括在内。他的解释有点简明扼要,我花了一段时间才弄清楚细节,所以我画了一些图表,以方便其他可能正在挣扎的人。我在社区维基上发表了这篇文章

    我将使用以下带有六个顶点的图作为示例。其中一条哈密顿路径以粗体显示


    给定一个有n个顶点的无向图,我们想要找到它的哈密顿路径,我们构造了一个新的加权有向图,其中有n2个顶点,加上起始点和结束点。标记原始顶点vi和新顶点wik为0≤i,k更新:OP现在已经进行了几轮澄清,这是一个不同的问题。我将把这篇文章留在这里,以记录我对问题的第一个版本的想法(或者更确切地说,我对它的理解)。对于这个问题的当前版本,我将尝试一个新的答案。 更新结束

    遗憾的是,OP没有澄清一些悬而未决的问题。我将假设如下:

  • 重量为+/-1
  • n是顶点数
  • 第一个假设显然没有失去一般性,但它对n的值有很大影响(通过第二个假设)。在没有第一个假设的情况下,通过无限制地改变权重,即使是一个很小的(固定的)图也可以有任意长的解

    我提出的算法非常简单,类似于著名的图算法。虽然我不是图表专家,但我可能在某些地方用错了词。请随意纠正我

  • 对于源顶点,请记住成本为0。将(源,0)添加到待办事项列表
  • 从待办事项列表中弹出一项。跟随顶点的所有输出边,计算新成本c以到达新顶点v。如果新成本有效(c>=0和c n^2)。 当c>n时,在到达(v,c)之前路径上必须有一些循环,其中循环成本为0m2>-n


    此外,让v可以从源处到达,成本为0我想澄清几点:

  • 是的,可能存在负重量循环
  • n是边的数目
  • 权重是任意的,不仅仅是+1/-1
  • 如果问题有解决方案,则假设存在一条O(n)长度的路径。(n是边数)

  • 尽管人们已经证明不存在快速解决方案(除非p=NP)

    我认为对于大多数图(95%+),您应该能够相当快地找到解决方案

    我利用了以下事实:
    def f(vertices, edges, source, dest):
        # vertices: unique hashable objects
        # edges: mapping (u, v) -> cost; u, v in vertices, cost in {-1, 1}
    
        #vertex_costs stores the possible costs for each vertex
        vertex_costs = dict((v, set()) for v in vertices)
        vertex_costs[source].add(0) # source can be reached with cost 0
    
        #vertex_costs_from stores for each the possible costs for each vertex the previous vertex
        vertex_costs_from = dict()
    
        # vertex_gotos is a convenience structure mapping a vertex to all ends of outgoing edges and their cost
        vertex_gotos = dict((v, []) for v in vertices)
        for (u, v), c in edges.items():
            vertex_gotos[u].append((v, c))
    
        max_c = len(vertices) ** 2 # the crucial number: maximal cost that's possible for an optimal path
    
        todo = [(source, 0)] # which vertices to look at
    
        while todo:
            u, c0 = todo.pop(0)
            for v, c1 in vertex_gotos[u]:
                c = c0 + c1
                if 0 <= c <= max_c and c not in vertex_costs[v]:
                    vertex_costs[v].add(c)
                    vertex_costs_from[v, c] = u
                    todo.append((v, c))
    
        if not vertex_costs[dest]: # destination not reachable
            return None # or raise some Exception
    
        cost = min(vertex_costs[dest])
    
        path = [(dest, cost)] # build in reverse order
        v, c = dest, cost
        while (v, c) != (source, 0):
            u = vertex_costs_from[v, c]
            c -= edges[u, v]
            v = u
            path.append((v, c))
    
        return path[::-1] # return the reversed path
    
    AB+ BC+ CD+ DA+             AX+ XY+ YH+ HI- IJ- JK- KL- LM- MH-
     A  B  C  D  A  X  Y  H  I  J  K  L  M  H
     0  1  2  3  4  5  6  7  6  5  4  3  2  1
    AB+ BC+ CD+ DE+ EF+ FG+ GA+ AX+ XY+ YH+ HI- IJ- JK- KL- LM- MH-
     A  B  C  D  E  F  G  A  B  C  D  E  F  G  A  B  C  D  E  F  G  A  X  Y  H  I  J  K  L  M  H  I  J  K  L  M  H  I  J  K  L  M  H  I  J  K  L  M  H
     0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
    AB+ BC+ CD+ DE+ EF+ FG+ GA+ AX+ XY+ YH+ HI- IJ- JK- KL- LM- MN- NH-
     A  X  Y  H
     0  1  2  3
    AB+ BC+ CD+ DE+ EF+ FG+ GA+ AX+ XY+ YH+ HI- IJ- JK- KL- LM- MN- NO- OP- PH-
     A  B  C  D  E  F  G  A  B  C  D  E  F  G  A  B  C  D  E  F  G  A  B  C  D  E  F  G  A  B  C  D  E  F  G  A  B  C  D  E  F  G  A  X  Y  H  I  J  K  L  M  N  O  P  H  I  J  K  L  M  N  O  P  H  I  J  K  L  M  N  O  P  H  I  J  K  L  M  N  O  P  H  I  J  K  L  M  N  O  P  H
     0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
    
    def find_path(edges, source, path):
        from itertools import chain
    
        print(edges)
        edges = dict(((u, v), 1 if c == "+" else -1) for u, v, c in edges.split())
        vertices = set(chain(*edges))
    
        path = f(vertices, edges, source, dest)
        path_v, path_c = zip(*path)
        print(" ".join("%2s" % v for v in path_v))
        print(" ".join("%2d" % c for c in path_c))
    
    source, dest = "AH"
    
    edges = "AB+ BC+ CD+ DA+             AX+ XY+ YH+ HI- IJ- JK- KL- LM- MH-"
    # uv+ means edge from u to v exists and has cost 1, uv- = cost -1
    find_path(edges, source, path)
    
    edges = "AB+ BC+ CD+ DE+ EF+ FG+ GA+ AX+ XY+ YH+ HI- IJ- JK- KL- LM- MH-"
    find_path(edges, source, path)
    
    edges = "AB+ BC+ CD+ DE+ EF+ FG+ GA+ AX+ XY+ YH+ HI- IJ- JK- KL- LM- MN- NH-"
    find_path(edges, source, path)
    
    edges = "AB+ BC+ CD+ DE+ EF+ FG+ GA+ AX+ XY+ YH+ HI- IJ- JK- KL- LM- MN- NO- OP- PH-"
    find_path(edges, source, path)
    
    Ideas:
    
        Now we have cases:
    a) there is both the positive and negative cycles were found:
    The answer is d(end,negC).
    
    b) no cycles were found:
    simply use shortest path algorithm?
    
    c) Only one of the cycles was found. We note in both these cases the problem is the same due to symmetry (e.g. if we swap the weights and start/end we get the same problem). I'll just consider the case that there was a positive cycle found.
    
    find the shortest path from start to end without going around the positive cycle. (perhaps using modified breadth first search or something). If no such path exists (without going positive).. then .. it gets a bit tricky.. we have to do laps of the positive cycle (and perhaps some percentage of a lap).
    If you just want an approximate answer, work out shortest path from positive cycle to end node which should usually be some negative number. Calculate number of laps required to overcome this negative answer + the distance from the entry point to the cycle to the exit point of the cycle. Now to do better perhaps there was another node in the cycle you should have exited the cycle from... To do this you would need to calculate the smallest negative distance of every node in the cycle to the end node.. and then it sort of turns into a group theory/ random number generator type problem... do as many laps of the cycle as you want till you get just above one of these numbers.