Problem of The Day: Path with Maximum Probability
Problem Statement

Brute Force - TLE
class Solution:
def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start_node: int, end_node: int) -> float:
graph = {i: [] for i in range(n)}
for i, [x, y] in enumerate(edges):
graph[x].append([y, succProb[i]])
graph[y].append([x, succProb[i]])
def dfs(start, end, graph, visited, curr):
if start == end:
dfs.max_prob = max(dfs.max_prob, curr)
return
visited.add(start)
for nei, p in graph[start]:
if nei not in visited:
dfs(nei, end, graph, visited, curr * p)
visited.remove(start)
dfs.max_prob = float('-inf')
dfs(start_node, end_node, graph, set(), 1)
return dfs.max_prob if dfs.max_prob != float('-inf') else 0.0
Dijkstra’s algorithm review
import heapq
def dijkstra(graph, start):
# Initialize distances and priority queue
distances = {vertex: float('infinity') for vertex in graph}
distances[start] = 0
priority_queue = [(0, start)]
while priority_queue:
current_distance, current_vertex = heapq.heappop(priority_queue)
# Nodes can get added to the priority queue multiple times. We only process a vertex the first time we remove it from the priority queue.
if current_distance > distances[current_vertex]:
continue
for neighbor, weight in graph[current_vertex].items():
distance = current_distance + weight
# Only consider this new path if it's better
if distance < distances[neighbor]:
distances[neighbor] = distance
heapq.heappush(priority_queue, (distance, neighbor))
return distances
# Example usage
graph = {
'A': {'B': 1, 'C': 4},
'B': {'A': 1, 'C': 2, 'D': 5},
'C': {'A': 4, 'B': 2, 'D': 1},
'D': {'B': 5, 'C': 1}
}
print(dijkstra(graph, 'A'))
Editorial
Approach 1: Bellman-Ford Algorithm
class Solution:
def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start: int, end: int) -> float:
max_prob = [0] * n
max_prob[start] = 1
for i in range(n - 1):
# If there is no larger probability found during an entire round of updates,
# stop the update process.
has_update = 0
for j in range(len(edges)):
u, v = edges[j]
path_prob = succProb[j]
if max_prob[u] * path_prob > max_prob[v]:
max_prob[v] = max_prob[u] * path_prob
has_update = 1
if max_prob[v] * path_prob > max_prob[u]:
max_prob[u] = max_prob[v] * path_prob
has_update = 1
if not has_update:
break
return max_prob[end]
Approach 2: Shortest Path Faster Algorithm
class Solution:
def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start: int, end: int) -> float:
graph = defaultdict(list)
for i, (a, b) in enumerate(edges):
graph[a].append([b, succProb[i]])
graph[b].append([a, succProb[i]])
max_prob = [0.0] * n
max_prob[start] = 1.0
queue = deque([start])
while queue:
cur_node = queue.popleft()
for nxt_node, path_prob in graph[cur_node]:
# Only update max_prob[nxt_node] if the current path increases
# the probability of reach nxt_node.
if max_prob[cur_node] * path_prob > max_prob[nxt_node]:
max_prob[nxt_node] = max_prob[cur_node] * path_prob
queue.append(nxt_node)
return max_prob[end]
Approach 3: Dijkstra’s Algorithm
class Solution:
def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start: int, end: int) -> float:
graph = defaultdict(list)
for i, (u, v) in enumerate(edges):
graph[u].append((v, succProb[i]))
graph[v].append((u, succProb[i]))
max_prob = [0.0] * n
max_prob[start] = 1.0
pq = [(-1.0, start)]
while pq:
cur_prob, cur_node = heapq.heappop(pq)
if cur_node == end:
return -cur_prob
for nxt_node, path_prob in graph[cur_node]:
if -cur_prob * path_prob > max_prob[nxt_node]:
max_prob[nxt_node] = -cur_prob * path_prob
heapq.heappush(pq, (-max_prob[nxt_node], nxt_node))
return 0.0
Leave a comment