Bellman-Ford
This session covers the Bellman-Ford algorithm for finding shortest paths in graphs with negative edge weights and detecting negative cycles.
Problems
Monk’s Business Day
Problem Information
- Source: HackerEarth
- Difficulty: Secret
- Time Limit: 1000ms
- Memory Limit: 256MB
Problem Statement
Monk is planning a business trip between cities. Each road has a profit/cost. Determine if Monk can make infinite profit by traveling in cycles (arbitrage). Essentially, detect if there’s a positive cycle in the graph.
Input Format
- T: number of test cases
- For each test case:
- N M: number of cities and roads
- M lines with i j C: road from i to j with profit C
Output Format
- “Yes” if infinite profit is possible (positive cycle exists), “No” otherwise
Example
Input:
2
3 3
1 2 5
2 3 2
3 1 4
3 3
1 2 5
2 3 2
3 1 -4
Output:
Yes
No
First case: Cycle 1->2->3->1 with total profit 5+2+4=11 > 0, so infinite profit possible. Second case: Cycle has profit 5+2+(-4)=3 but no positive cycle that gives infinite arbitrage.
Solution
Approach
Use Bellman-Ford algorithm with negated edge weights. Negate weights to convert profit maximization to shortest path. After N-1 iterations, if any edge can still be relaxed, a negative cycle exists. Negative cycle in negated graph = positive cycle in original = infinite profit.
Python Solution
import sys
INF = float('inf')
def bellman_ford(n, edges):
dist = [INF] * (n + 1)
dist[1] = 0
for _ in range(n - 1):
for u, v, w in edges:
if dist[u] != INF and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
# Check for negative cycle
for u, v, w in edges:
if dist[u] != INF and dist[u] + w < dist[v]:
return 'Yes'
return 'No'
def solution():
tokens = sys.stdin.read().split()[::-1]
t = int(tokens.pop())
for _ in range(t):
n, m = int(tokens.pop()), int(tokens.pop())
edges = [
(int(tokens.pop()), int(tokens.pop()), -int(tokens.pop()))
for _ in range(m)
]
print(bellman_ford(n, edges))
solution()
Complexity Analysis
- Time Complexity: O(V * E) where V is vertices and E is edges
- Space Complexity: O(V + E)
Single Source Shortest Path, Negative Weights
Problem Information
- Source: Kattis
- Difficulty: Secret
- Time Limit: 2000ms
- Memory Limit: 256MB
Problem Statement
Given a directed weighted graph with possible negative edge weights, find the shortest path from source to multiple query nodes. Handle negative cycles by reporting “-Infinity” for affected nodes.
Input Format
- Multiple test cases until n=0
- For each test case:
- n m q s: nodes, edges, queries, source
- m lines with u v w: directed edge from u to v with weight w
- q query nodes
Output Format
- For each query:
- Shortest distance, or “Impossible” if unreachable, or “-Infinity” if affected by negative cycle
Example
Input:
5 4 3 0
0 1 999
1 2 -2
2 1 1
0 3 2
1
3
4
Output:
-Infinity
2
Impossible
Node 1 is affected by negative cycle (1->2->1 with weight -2+1=-1). Node 3 has shortest distance 2 from source 0. Node 4 is unreachable from source.
Solution
Approach
Use Bellman-Ford algorithm for single-source shortest path with negative weights. Run N-1 iterations for standard relaxation. Run one more iteration to detect nodes affected by negative cycles. Nodes that can still be relaxed are affected by negative cycles.
Python Solution
import sys
INF = float('inf')
def bellman_ford(n, edges, queries):
dist = [INF] * (n + 1)
neg_cycle = [False] * (n + 1)
dist[0] = 0
for _ in range(n - 1):
for u, v, w in edges:
if dist[u] != INF and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
# Detect nodes affected by negative cycles
for u, v, w in edges:
if dist[u] != INF and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
neg_cycle[v] = True
for q in queries:
if neg_cycle[q]:
print('-Infinity')
elif dist[q] == INF:
print('Impossible')
else:
print(dist[q])
def solution():
tokens = sys.stdin.read().split()[::-1]
while True:
n = int(tokens.pop())
if n == 0:
break
m, num_queries, _ = int(tokens.pop()), int(tokens.pop()), int(tokens.pop())
edges = [
(int(tokens.pop()), int(tokens.pop()), int(tokens.pop()))
for _ in range(m)
]
queries = [int(tokens.pop()) for _ in range(num_queries)]
bellman_ford(n, edges, queries)
print()
solution()
Complexity Analysis
- Time Complexity: O(V * E) per test case
- Space Complexity: O(V + E)
Extended Traffic
Problem Information
- Source: LightOJ
- Difficulty: Secret
- Time Limit: 2000ms
- Memory Limit: 32MB
Problem Statement
Dhaka city has junctions with “busyness” values. The cost to travel from junction u to v is (busyness[v] - busyness[u])^3, which can be negative. Find the minimum cost from junction 1 to query junctions. If cost < 3 or unreachable or affected by negative cycle, output “?”.
Input Format
- T: number of test cases
- For each test case:
- N: number of junctions
- N busyness values
- M: number of roads
- M lines with u v: directed road from u to v
- Q: number of queries
- Q query junction numbers
Output Format
- For each case: “Case X:” followed by minimum cost for each query (or “?”)
Example
Input:
1
5
6 7 8 2 4
6
1 2
2 3
3 4
1 3
1 4
4 5
2
4
5
Output:
Case 1:
?
4
From junction 1: to reach junction 4, cost is negative (busyness differences cubed can be negative), so output “?”. To junction 5, minimum cost is 4.
Solution
Approach
Use Bellman-Ford algorithm to handle negative edge weights. Edge weight = (busyness[v] - busyness[u])^3. Detect negative cycles and mark affected nodes. Output “?” for unreachable, negative cycle affected, or cost < 3.
Python Solution
import sys
INF = float('inf')
def bellman_ford(n, edges, queries, case_number):
dist = [INF] * (n + 1)
neg_cycle = [False] * (n + 1)
print(f'Case {case_number}:')
dist[1] = 0
for _ in range(n - 1):
for u, v, w in edges:
if dist[u] != INF and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
for u, v, w in edges:
if dist[u] != INF and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
neg_cycle[v] = True
for q in queries:
if neg_cycle[q] or dist[q] < 3 or dist[q] == INF:
print('?')
else:
print(dist[q])
def solution():
tokens = sys.stdin.read().split()[::-1]
t = int(tokens.pop())
for case in range(1, t + 1):
n = int(tokens.pop())
busyness = [0] + [int(tokens.pop()) for _ in range(n)]
m = int(tokens.pop())
edges = []
for _ in range(m):
i, j = int(tokens.pop()), int(tokens.pop())
edges.append((i, j, busyness[j] - busyness[i]))
num_queries = int(tokens.pop())
queries = [int(tokens.pop()) for _ in range(num_queries)]
bellman_ford(n, edges, queries, case)
solution()
Complexity Analysis
- Time Complexity: O(T * V * E) where T is test cases
- Space Complexity: O(V + E)
Babylon Tours
Problem Information
- Source: SPOJ
- Difficulty: Secret
- Time Limit: 1000ms
- Memory Limit: 1536MB
Problem Statement
Given N monuments in Babylon with travel costs between them (can be negative), find the shortest path for multiple tour queries. Detect if a query path is affected by a negative cycle.
Input Format
- Multiple test cases until N=0
- For each test case:
- N: number of monuments
- N lines: monument name followed by N costs (0 means no direct path)
- Q: number of queries
- Q lines with source and destination monument indices
Output Format
- For each case: “Case #X:” followed by results for each query
- “monument1-monument2 cost” or “NOT REACHABLE” or “NEGATIVE CYCLE”
Example
Input:
3
Sphinx 10 -2 0
Move 0 5 -3
Pyramid 0 0 0
2
0 1
1 2
Output:
Case #1:
Sphinx-Move 10
Move-Pyramid -3
Travel cost from Sphinx to Move is 10. Travel cost from Move to Pyramid is -3.
Solution
Approach
Use Bellman-Ford algorithm for each unique source in queries. Handle negative edge weights and detect negative cycles. Cache results to avoid recomputing for same source.
Python Solution
from collections import defaultdict
INF = float('inf')
def bellman_ford(n, edges, source):
dist = [INF] * (n + 1)
neg_cycle = [False] * (n + 1)
dist[source] = 0
for _ in range(n - 1):
for u, v, w in edges:
if dist[u] != INF and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
for u, v, w in edges:
if dist[u] != INF and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
neg_cycle[v] = True
return dist, neg_cycle
def solution():
case_num = 1
while True:
n = int(input())
if n == 0:
break
edges = []
monuments = []
for x in range(n):
line = input().split()
monuments.append(line[0])
for i, cost in enumerate(line[1:], start=0):
cost = int(cost)
if cost != 0 and (cost < 0 or x != i):
edges.append((x, i, cost))
num_queries = int(input())
query_list = []
unique_sources = defaultdict(list)
for _ in range(num_queries):
src, dest = map(int, input().split())
query_list.append((src, dest))
unique_sources[src].append(dest)
print(f'Case #{case_num}:')
# Cache results per source
dist_cache = {}
neg_cache = {}
for source in unique_sources:
dist_cache[source], neg_cache[source] = bellman_ford(n, edges, source)
for src, dest in query_list:
dist = dist_cache[src][dest]
is_neg = neg_cache[src][dest]
if (dist < 0 and src == dest) or is_neg:
print("NEGATIVE CYCLE")
elif dist == INF:
print(f"{monuments[src]}-{monuments[dest]} NOT REACHABLE")
else:
print(f"{monuments[src]}-{monuments[dest]} {dist}")
case_num += 1
solution()
Complexity Analysis
- Time Complexity: O(Q * V * E) where Q is unique query sources
- Space Complexity: O(V^2) for caching distances
106 Miles To Chicago
Problem Information
- Source: URI Online Judge
- Difficulty: Secret
- Time Limit: 1000ms
- Memory Limit: 256MB
Problem Statement
A spy needs to travel from intersection 1 to intersection n through Chicago. Each road segment has a probability of not being spotted (given as percentage). Find the maximum probability of traveling the entire path without being spotted. The total probability is the product of individual segment probabilities.
Input Format
- Multiple test cases until n=0
- For each test case:
- n m: number of intersections and streets
- m lines with a b p: street between a and b with p% probability of not being spotted
Output Format
- Maximum probability (as percentage) of reaching n from 1 unspotted
Example
Input:
5 7
1 2 50
2 3 50
3 4 50
4 5 50
1 3 80
2 4 80
3 5 80
Output:
51.200000 percent
Best path: 1->3 (80%) -> 4 (80%) -> 5 (80%) = 0.8 * 0.8 * 0.8 * 100 = 51.2% probability.
Solution
Approach
Use modified Bellman-Ford to maximize product of probabilities. Initialize source with 100% probability. Relax edges by multiplying probabilities instead of adding distances. Edges are bidirectional.
Python Solution
import sys
NEG_INF = float('-inf')
def bellman_ford(n, edges):
dist = [NEG_INF] * (n + 1)
dist[1] = 100
for _ in range(n - 1):
for u, v, prob in edges:
if dist[u] != NEG_INF and dist[u] * prob / 100 > dist[v]:
dist[v] = dist[u] * prob / 100
return f"{dist[n]:.6f} percent"
def solution():
tokens = sys.stdin.read().split()[::-1]
while True:
n = int(tokens.pop())
if n == 0:
break
m = int(tokens.pop())
edges = []
for _ in range(m):
a, b, p = int(tokens.pop()), int(tokens.pop()), int(tokens.pop())
edges.extend([(a, b, p), (b, a, p)]) # Bidirectional
print(bellman_ford(n, edges))
solution()
Complexity Analysis
- Time Complexity: O(V * E)
- Space Complexity: O(V + E)
XYZZY
Problem Information
- Source: UVA
- Difficulty: Secret
- Time Limit: 3000ms
- Memory Limit: 256MB
Problem Statement
A text adventure game where the player starts in room 1 with 100 energy. Moving to a room adds/subtracts energy based on the room’s energy value. The player dies if energy drops to 0 or below. Determine if the player can reach the last room (room n) with positive energy.
Input Format
- Multiple test cases until n=-1
- For each test case:
- n: number of rooms
- For each room: energy_value, num_connections, connected_rooms…
Output Format
- “winnable” if player can reach room n with positive energy, “hopeless” otherwise
Example
Input:
5
0 1 2
-60 1 3
-60 1 4
20 1 5
0 0
Output:
winnable
Player starts in room 1 with 100 energy. Path: 1->2 (100-60=40) -> 3 (40-60=-20, dies) OR 1->2->4->5 with energy management. The game is winnable.
Solution
Approach
Use modified Bellman-Ford to maximize energy (find longest path). Only traverse edges if current energy > 0. If a positive cycle exists that can reach room n, game is winnable. Detect positive cycles that allow infinite energy gain.
Python Solution
import sys
NEG_INF = float('-inf')
def bellman_ford(n, edges):
dist = [NEG_INF] * n
dist[0] = 100
for _ in range(n - 1):
for u, v, energy in edges:
if dist[u] != NEG_INF and dist[u] + energy > dist[v] and dist[u] + energy > 0:
dist[v] = dist[u] + energy
# Check for positive cycle (can gain infinite energy)
for u, v, energy in edges:
if dist[u] != NEG_INF and dist[u] + energy > dist[v] != NEG_INF:
return 'winnable'
return 'hopeless' if dist[n - 1] < 0 else 'winnable'
def solution():
tokens = sys.stdin.read().split()[::-1]
while True:
n = int(tokens.pop())
if n == -1:
break
edges = []
energies = []
for i in range(n):
energy = int(tokens.pop())
energies.append(energy)
num_connections = int(tokens.pop())
for _ in range(num_connections):
neighbor = int(tokens.pop()) - 1
edges.append([i, neighbor])
# Add energy values to edges
for edge in edges:
edge.append(energies[edge[1]])
print(bellman_ford(n, edges))
solution()
Complexity Analysis
- Time Complexity: O(V * E)
- Space Complexity: O(V + E)
MPI Maelstrom
Problem Information
- Source: UVA
- Difficulty: Secret
- Time Limit: 3000ms
- Memory Limit: 256MB
Problem Statement
N processors are connected in a network. The communication time between processors is given in a lower triangular matrix format. Some connections may not exist (marked as ‘x’). Find the minimum time to broadcast a message from processor 0 to all other processors (maximum shortest path from source).
Input Format
- N: number of processors
- Lower triangular matrix of communication times (‘x’ means no connection)
Output Format
- Maximum shortest path distance from processor 0 (broadcast completion time)
Example
Input:
5
50
30 5
100 20 50
10 x x 10
Output:
35
Shortest paths from processor 0: to 1 is 50, to 2 is 35 (0->1->2 = 50+5=55 or 0->4->3 = 10+10+20=40… wait, 0->2 = 30), to 3 is 20, to 4 is 10. Maximum is 35 (to processor 2 via 0->1->2 = 50+5=55, but directly 30 is better, final max is from all shortest paths).
Solution
Approach
Use Bellman-Ford algorithm. Build undirected graph from lower triangular matrix. Find shortest paths from processor 0 to all others. Return the maximum of all shortest path distances.
Python Solution
INF = float('inf')
def bellman_ford(n, edges):
dist = [INF] * n
dist[0] = 0
for _ in range(n - 1):
for u, v, w in edges:
if dist[u] != INF and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
return max(dist)
def solution():
n = int(input())
edges = []
for i in range(n - 1):
line = input().strip().split()
for j, val in enumerate(line[:i + 1]):
if val != 'x':
cost = int(val)
edges.extend([(i + 1, j, cost), (j, i + 1, cost)])
print(bellman_ford(n, edges))
solution()
Complexity Analysis
- Time Complexity: O(V * E)
- Space Complexity: O(V + E)
Wormholes
Problem Information
- Source: UVA
- Difficulty: Secret
- Time Limit: 3000ms
- Memory Limit: 256MB
Problem Statement
A spaceship can travel through wormholes that may have negative time travel (going back in time). Determine if it’s possible to travel back in time indefinitely by finding a negative cycle in the graph.
Input Format
- T: number of test cases
- For each test case:
- n m: number of star systems and wormholes
- m lines with x y t: wormhole from x to y with time t (can be negative)
Output Format
- “possible” if negative cycle exists (infinite time travel possible)
- “not possible” otherwise
Example
Input:
2
3 3
0 1 1000
1 2 15
2 1 -42
4 4
0 1 10
1 2 20
2 3 30
3 0 -60
Output:
possible
not possible
First case: Cycle 1->2->1 with weight 15+(-42)=-27 is a negative cycle. Second case: Cycle 0->1->2->3->0 with weight 10+20+30+(-60)=0, not negative.
Solution
Approach
Use Bellman-Ford algorithm to detect negative cycles. Run N-1 iterations of relaxation. If any edge can still be relaxed in iteration N, a negative cycle exists.
Python Solution
import sys
INF = float('inf')
def bellman_ford(n, edges):
dist = [INF] * (n + 1)
dist[0] = 0
for _ in range(n - 1):
for u, v, w in edges:
if dist[u] != INF and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
# Check for negative cycle
for u, v, w in edges:
if dist[u] != INF and dist[u] + w < dist[v]:
return 'possible'
return 'not possible'
def solution():
tokens = sys.stdin.read().split()[::-1]
t = int(tokens.pop())
for _ in range(t):
n, m = int(tokens.pop()), int(tokens.pop())
edges = [
(int(tokens.pop()), int(tokens.pop()), int(tokens.pop()))
for _ in range(m)
]
print(bellman_ford(n, edges))
solution()
Complexity Analysis
- Time Complexity: O(T * V * E)
- Space Complexity: O(V + E)