📅  最后修改于: 2023-12-03 15:10:40.957000             🧑  作者: Mango
给定一个带权有向图,每一条边的权重大于等于1,需要求从起点开始到终点结束的一条路径,使得该路径所经过的边的权重乘积最小。如果存在多条满足条件的路径,输出其中任意一条。
使用Dijkstra算法求解最短路径的思路有些相似,不过有以下两个要点需要注意:
因此,我们可以采用修改版的Dijkstra算法,根据当前点到起点的路径乘积来选出下一个顶点,同时使用堆来保存待选顶点集合,每一次取出来的顶点都是到起点的路径乘积最小的。而为了避免出现溢出的问题,我们每一步都对中间结果进行取模运算。
我们定义下列变量:
首先,我们需要定义一个待选顶点集合,用堆来实现。每次从堆中取出距离源点近、到起点的路径乘积小的顶点,并将其标记为已处理,同时将与其相邻的顶点(未被标记过)加入到待选顶点集合中。最终,堆中的元素都会被处理,此时到起点的路径乘积最小的那个顶点,就是从起点到终点的最短路径。
from queue import PriorityQueue
from typing import List
n = 0 # 图中节点的总数
source = 0 # 起点编号
target = 0 # 终点编号
INF = float("inf")
base = 1000000007 # 取模的基数
# 图的邻接矩阵,Edge[i][j]表示i到j边的权重
edges = [[0] * (n+1) for _ in range(n+1)]
# 记录到每一个点的最小距离
dist = [INF] * (n+1)
# 用于记录路径的数组
path = [-1] * (n+1)
# 标记是否被处理过
seen = [False] * (n+1)
# 定义一个堆
heap = PriorityQueue()
从起点开始,路径长度为0,到起点的路径乘积为1。将其插入到堆中,并将其到起点的路径乘积设置为1。
dist[source] = 0
seen[source] = True
heap.put((-1, source, 1))
在接下来的每一步中,我们从堆中选择距离源点最近,到起点的路径乘积最小的顶点。对于每一条从当前顶点出发的边,我们计算到下一个顶点的路径乘积,并且将其插入到堆中,以便后续的处理。如果到达终点,遍历结束。
while not heap.empty():
_, node, prod = heap.get() # 从堆中取出距离源点近、到起点的路径乘积小的顶点
if node == target: # 到达终点
break
seen[node] = True
# 遍历所有的邻居节点
for nei in range(1, n+1):
# 如果邻居节点已经处理过了,则跳过
if seen[nei]:
continue
# 计算到邻居节点的路径乘积
new_prod = prod * edges[node][nei] % base
if new_prod < dist[nei]:
dist[nei] = new_prod
path[nei] = node
heap.put((-dist[nei], nei, new_prod))
到达终点后,我们可以通过$path$数组从终点逆序找到路径的具体路线。我们定义一个列表$path$,从终点开始逆序遍历至起点,最终得到的就是从起点到终点的最短路径。
# 从终点开始逆序遍历,恢复具体路径
res = [target]
while path[target] != -1:
target = path[target]
res.append(target)
res.reverse()
# 返回最短路径
return res
from queue import PriorityQueue
from typing import List
def min_product_path(n: int, edges: List[List[int]], source: int, target: int) -> List[int]:
INF = float("inf")
base = 1000000007
dist = [INF] * (n+1)
path = [-1] * (n+1)
seen = [False] * (n+1)
heap = PriorityQueue()
edges_mtx = [[0] * (n+1) for _ in range(n+1)]
for i, j, w in edges:
edges_mtx[i][j] = w
edges = edges_mtx
# 初始化起点
dist[source] = 0
seen[source] = True
heap.put((-1, source, 1)) # 用(-dist, node, prod)的元组来进行排序
# 进行Dijkstra遍历
while not heap.empty():
_, node, prod = heap.get() # 从堆中取出距离源点近、到起点的路径乘积小的顶点
if node == target: # 到达终点
break
seen[node] = True
# 遍历所有的邻居节点
for nei in range(1, n+1):
# 如果邻居节点已经处理过了,则跳过
if seen[nei]:
continue
# 计算到邻居节点的路径乘积
new_prod = prod * edges[node][nei] % base
if new_prod < dist[nei]:
dist[nei] = new_prod
path[nei] = node
heap.put((-dist[nei], nei, new_prod))
# 从终点开始逆序遍历,恢复具体路径
res = [target]
while path[target] != -1:
target = path[target]
res.append(target)
res.reverse()
# 返回最短路径
return res