【问题标题】:In a directed, weighted graph, efficiently find the cost of the shortest path between two nodes A and B that traverses through a node X在有向加权图中,有效地找到两个节点 A 和 B 之间穿过节点 X 的最短路径的成本
【发布时间】:2021-02-16 18:03:08
【问题描述】:

我是一名初学者,但仍在掌握路径算法的概念。我们的教授给了我们这个问题,在线法官将评估哪个解决方案:

我得到以下信息:

  • 节点数L(每个节点以数字命名,1-indexed)
    • 2 <= L <= 1000
  • 从一个节点到另一个节点的有向边R,以及边的成本C
    • 0 <= R <= L * sqrt(L)
    • 1 <= C <= 100
    • 边数与节点数一起给出,以便轻松收集有向边。
    • 没有负成本。
  • 源节点A
  • 目的节点B
  • 遍历的节点X

我需要找出从 A 到 X 到 B 的最短路径的成本。

我的代码运行如下:

  1. 获取输入。
  2. 生成图的邻接表。
  3. 获取 A、X 和 B。
  4. 获取 A 和 X 之间最短路径的成本ax
  5. 获取 X 和 B 之间最短路径的成本xb
  6. 从 A 到 X 到 B 的最短路径的成本 axbax + xb

法官认为这个解决方案超过了 1 秒的时间限制。有没有办法改进它并提高效率?

  • 我考虑过将 X 设为源节点,但该图是有向的,而取消指向它会产生错误的结果。

  • 我考虑过检查 A 和 X 之间的路径是否存在,然后是 X 和 B,但它似乎对性能的影响可以忽略不计。

  • 我还考虑过“钳制”图表,使其仅包含 A 和 B 之间经过 X 的路径,但我不知道如何以最有效的方式做到这一点。

  • 我尝试过应用this idea,但它只是让它运行时间更长。

  • 我的教授说 A* 对他给我们提出的问题来说太过分了,但如果我为此找到一个启发式方法,也许我会考虑使用它。

  • 我尝试了 Floyd-Warshall 算法,但它使代码消耗了更多的时间和内存——但有件事告诉我这个算法可以优化:

def shortestPath(dist, i, j, k): # My first try in applying the Floyd-Warshall Algorithm

    for k in range(len(dist)):
        for i in range(len(dist)):
            for j in range(len(dist)):
                if dist[i][j] > dist[i][k] + dist[k][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]

# Graph construction and algorithm application:
    .
    .
    .
    # generate adjacency list of the graph
    graph = [[math.inf for node_x in range(nodes)] for node_y in range(nodes)]
    next = [[None for node_x in range(nodes)] for node_y in range(nodes)]
    for node in range(nodes):
        graph[node][node] = 0

    for edge in range(edges):
        node_a, node_b, cost = input().split()
        node_a = int(node_a) - 1
        node_b = int(node_b) - 1
        cost = eval(cost)
        graph[node_a][node_b] = cost
        next[node_a][node_b] = node_b

    i, k, j = input().split()  # get source, traversed and destination node
    i = int(i) - 1
    j = int(j) - 1
    k = int(k) - 1
    shortestPath(graph, i, j, k)
    ikj = graph[i][j]

    if ikj == math.inf:  # path nonexistent if either ax or xb is infinite
        out.append("path nonexistent")
        continue
    else:
        out.append(ikj)

这是我的代码:

import heapq
import math


# tools start here

def dijkstra(G, S):
    pq = []
    costs = {v: math.inf for v in G}
    costs[S] = 0

    heapq.heappush(pq, (0, S))
    while pq:
        d_u, u = heapq.heappop(pq)
        for e in G[u]:
            v, w = e
            d_v = costs[v]
            if d_v > d_u + w:  # relaxation operation
                costs[v] = d_u + w
                heapq.heappush(pq, (d_u + w, v))
    return costs


# tools end here

t = int(input())  # get number of test cases
out = []  # initialize output array
for _ in range(t): # for each test case

    # get input
    nodes, edges = input().split()
    nodes = int(nodes)
    edges = int(edges)

    # generate adjacency list of the graph
    graph = {}
    for node in range(1, nodes + 1):
        graph[str(node)] = []

    for edge in range(edges):
        node_a, node_b, cost = input().split()
        cost = eval(cost)
        graph[node_a].append((node_b, cost,))

    a, x, b = input().split()  # get source, traversed and destination node

    ax = dijkstra(graph, a)[x]  # get shortest path cost from a to x
    xb = dijkstra(graph, x)[b]  # get shortest path cost from x to b

    axb = ax + xb  # add the path costs

    if axb == math.inf:  # path nonexistent if either ax or xb is infinite
        out.append("path nonexistent")
        continue
    else:
        out.append(axb)

[print(_) for _ in out]  # print output array

这里是示例输入:

2
3 2
1 2 1
2 3 1
1 2 3
3 1
1 2 1
1 2 3

这是示例输出:

2
path nonexistent

【问题讨论】:

  • 如果没有一些示例输入,很难理解您的问题。请编辑您的问题并显示生成的输入和输出的示例。
  • 真正的问题是什么?根据这个问题,您似乎已经解决了您的问题,但只是希望它更快。您是否尝试过 A*(假设您可以为您的图表找到启发式方法)?
  • 在线评委评价此方案超过时限。我不知道如何进一步优化这个解决方案。我不认为 A* 会起作用,因为没有办法跟踪离目的地有多近(或者该图是否存在另一种启发式方法?)
  • 可能想在这里发帖:stats.stackexchange.com
  • 我不认为这是一个统计问题...

标签: python python-3.x graph-theory shortest-path dijkstra


【解决方案1】:

我认为你的解决方案已经是最快的了。

如果您想从代码中挤出更多的运行时性能,我建议重新合并检查 A 和 X 之间的路径是否存在,然后是 X 和 B。

我还建议将您的 eval() 替换为 int(),因为考虑到您的问题的限制,假设检查它是否是 int 以外的任何东西会更慢。

最后,这很可能是一个在线法官,它接受的代码会在输出时打印输出,因此在每个测试用例之后立即输出答案。

希望有帮助!

【讨论】:

  • 我怎么没想到这个
  • ...显然你做到了。
猜你喜欢
  • 2015-02-19
  • 2012-12-18
  • 1970-01-01
  • 2015-04-04
  • 1970-01-01
  • 2017-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多