📅  最后修改于: 2023-12-03 15:28:39.670000             🧑  作者: Mango
这个主题针对的是GATE计算机科学2022考试中的一道题目。这题目的主要难点在于要求人们在时间复杂度O(nlogn)内解决问题。
有一个由n个节点和m条边组成的有向图。每个节点都是由介于1和n之间的数字标识的。现在我们需要找到从1号节点到n号节点的最短距离,并且还需要确定这个最短距离所经过的门数。一条边被称为一扇门,而每个点被视为打开哪些门的开关。
问题可以通过修改dijkstra算法来解决。我们可以用一个变量来记录到达每个节点时经过的门数。我们需要维护一个小根堆,其元素为一个2元组(distance, node)
,其中distance
表示到达该节点的最短距离,node
表示该节点的标识。
我们可以使用一个类来表示节点,每个节点都有一个唯一的标识符和一个字典,表示能到达它的其他节点及其门数。因此,我们可以遍历每个节点并构建该字典。这样,实际上我们已经能够在O(1)中询问跨门链接。
如果有足够的内存可用,则可以使用priority queue作为堆的实现。但是,由于Python heapq的限制,我们必须使用2倍最大的门数来帮助我们在元组中插入节点。因此,“距离”将使用一个大小为2的元组来表示(distance, door_count)
。
既然进入堆中的元素现在包括到目标的最短距离和要穿越的门的数量,我们使用简单的选择运算符来确定是否将更短的距离推入堆。我们将每个初次到达的节点的距离记录为MAX_INT或类似的东西,因此在首次访问时,每个节点的距离都将小于MAX_INT。
代码示例如下:
import heapq
from collections import defaultdict
N, M = 100000, 100000
g = defaultdict(dict)
dist = [float("inf")] * (N + 1)
min_doors = [float("inf")] * (N + 1)
# initialize nodes
for i in range(1, N+1):
g[i] = {}
# read input
for i in range(M):
u, v, doors = map(int, input().split())
if v not in g[u]:
g[u][v] = doors
else:
g[u][v] = min(g[u][v], doors)
# distance to start is 0, and number of doors passed is 0
heapq.heappush([(0, 0), 1])
dist[1] = min_doors[1] = 0
while heap:
(d, doors), u = heapq.heappop(heap)
# current node has already been updated with shorter distance
if dist[u] < d:
continue
for v, doors_travelled in g[u].items():
# new distance
alt = d + 1
# new door count
alt_doors = doors + doors_travelled
# new distance is shorter than previously found shortest path
if alt < dist[v] or (alt == dist[v] and alt_doors < min_doors[v]):
dist[v] = alt
min_doors[v] = alt_doors
heapq.heappush(heap, [(alt, alt_doors), v])
# optimal path distance and number of doors passed
print(dist[N], min_doors[N])
上述程序的时间复杂度为$ O(mlogn) $,但因为我们使用了稀疏图,所以在实际中应该快得多。