📅  最后修改于: 2023-12-03 15:42:22.136000             🧑  作者: Mango
这是一道经典的算法题,考察的是图论中的最短路径问题,也是一道比较基础的题目。
给定一个有向无环图,节点编号从 1 到 n,每个节点有一个权值 w[i],表示从起点到该节点的路径上会经过 w[i] 个门。现在你可以在任意门前停留,但是只能以 w[i] 的倍数时间前进。问从起点到终点的最短时间是多少?
第一行两个整数 n 和 m,表示节点数和边数。
接下来 m 行,每行三个整数 a, b, c,表示存在一条从 a 到 b 的有向边,边的长度为 c。
最后一行 n 个整数,依次表示每个节点的 w 值。
输出一个整数,表示从起点到终点的最短时间。
首先,这是一道图论中的最短路径问题,因此可以使用 Dijkstra 算法或者 Bellman-Ford 算法来求解。由于这是有向无环图,可以使用拓扑排序对节点进行排序,对于每个节点 u,依次遍历它的出边,进行松弛操作即可。时间复杂度为 O(m+n)。
其次,需要注意的是,节点上的权值 w[i] 表示从起点到该节点的路径上会经过 w[i] 个门,因此需要根据 w 值将节点分成若干个等价类,类内的节点可以互相到达,且经过门的数量相同。对于同一等价类内的节点,它们的最短路径长度是相同的。因此,遍历节点的时候,应该按照等价类的顺序进行遍历,这样可以确保遍历到某个节点时,它的前面的节点已经更新过了。
以下代码使用了拓扑排序和 Dijkstra 算法,时间复杂度为 O(m+n)。
from collections import defaultdict
import heapq
def topo_sort(graph):
# 拓扑排序
in_degrees = {node: 0 for node in graph}
for _, neighbors in graph.items():
for v in neighbors:
in_degrees[v] += 1
queue = [node for node, in_degree in in_degrees.items() if in_degree == 0]
order = []
while queue:
node = queue.pop(0)
order.append(node)
for v in graph[node]:
in_degrees[v] -= 1
if in_degrees[v] == 0:
queue.append(v)
return order
def dijkstra(graph, start, weights):
# Dijkstra 算法
heap = [(0, start)]
visited = set()
dist = {node: float('inf') for node in graph}
dist[start] = 0
while heap:
(d, node) = heapq.heappop(heap)
if node in visited:
continue
visited.add(node)
for neighbor, weight in graph[node]:
w = weights[neighbor-1]
if dist[node] % w != 0:
edge_len = w - dist[node] % w
else:
edge_len = w
if dist[node] + edge_len + weight < dist[neighbor]:
dist[neighbor] = dist[node] + edge_len + weight
heapq.heappush(heap, (dist[neighbor], neighbor))
return dist
n, m = map(int, input().split())
graph = defaultdict(list)
for _ in range(m):
a, b, c = map(int, input().split())
graph[a].append((b, c))
weights = list(map(int, input().split()))
order = topo_sort(graph)
dist = {node: float('inf') for node in graph}
dist[1] = 0
for node in order:
if dist[node] == float('inf'):
continue
for neighbor, weight in graph[node]:
w = weights[neighbor-1]
if dist[node] % w != 0:
edge_len = w - dist[node] % w
else:
edge_len = w
if dist[node] + edge_len + weight < dist[neighbor]:
dist[neighbor] = dist[node] + edge_len + weight
print(dist[n])
本题虽然是一道经典的算法题,但是它其实还是有一定难度的。需要注意图中存在等价类这个条件,并且需要根据等价类的顺序来遍历节点。同时,还需要注意时间的计算,需要根据 w 值来计算经过门的数量。