| Aspect | Details |
|---|---|
| Problem | Find maximum number of routes from city 1 to city n |
| Constraint | Each flight (edge) can be used at most once |
| Core Concept | Maximum flow with unit capacity edges |
| Key Insight | Max flow = Max edge-disjoint paths |
| Algorithm | Ford-Fulkerson with BFS (Edmonds-Karp) |
After completing this problem, you will understand:
Given n cities and m one-way flights between them, find the maximum number of distinct routes from city 1 to city n such that each flight is used at most once. Output the routes.
Input:
Output:
Constraints:
Example:
Input:
4 5
1 2
2 4
1 3
3 4
1 4
Output:
3
2 1 4
3 1 2 4
3 1 3 4
This problem is equivalent to finding the maximum flow in a network where each edge has capacity 1.
Why? Each unit of flow from source to sink represents one path. Since each edge has capacity 1, each edge can only be part of one path. Therefore:
Maximum Flow = Maximum Number of Edge-Disjoint Paths
Capacity 1 on all edges
[1] -----> [2]
| \ |
| \ |
v v v
[3] -----> [4]
Flow interpretation:
- Send 1 unit: 1 -> 4 (direct)
- Send 1 unit: 1 -> 2 -> 4
- Send 1 unit: 1 -> 3 -> 4
Total flow = 3 = 3 edge-disjoint paths
Using BFS guarantees we find the shortest augmenting path, which provides:
For each edge (u, v) with capacity c:
Original edge: u ---[cap=1]---> v
After flow of 1: u ---[cap=0]---> v (forward: full)
u <--[cap=1]--- v (reverse: can undo)
Cities: 1, 2, 3, 4
Flights: 1->2, 2->4, 1->3, 3->4, 1->4
+---[2]---+
| |
(1) (1)
| |
[1]-+ +->[4]
| |
(1) (1)
| |
+---[3]---+
|
(1)
|
+---------->
(number) = capacity = 1
Path 1: BFS finds 1 -> 4 (shortest)
[1] =========> [4]
Flow: 1
Edge 1->4 now used (capacity 0)
Path 2: BFS finds 1 -> 2 -> 4
[1] ---> [2] ---> [4]
Flow: 2
Edges 1->2, 2->4 now used
Path 3: BFS finds 1 -> 3 -> 4
[1] ---> [3] ---> [4]
Flow: 3
Edges 1->3, 3->4 now used
No more augmenting paths exist. Maximum flow = 3
Input:
n=4, m=5
Edges: (1,2), (2,4), (1,3), (3,4), (1,4)
Step-by-step:
| Step | Action | BFS Path Found | Flow | Used Edges |
|---|---|---|---|---|
| 0 | Initialize | - | 0 | {} |
| 1 | BFS from 1 | 1 -> 4 | 1 | {1->4} |
| 2 | BFS from 1 | 1 -> 2 -> 4 | 2 | {1->4, 1->2, 2->4} |
| 3 | BFS from 1 | 1 -> 3 -> 4 | 3 | {1->4, 1->2, 2->4, 1->3, 3->4} |
| 4 | BFS from 1 | None found | 3 | - |
Path Reconstruction:
Output:
3
2 1 4
3 1 2 4
3 1 3 4
from collections import deque, defaultdict
def solve():
n, m = map(int, input().split())
# Adjacency list with edge indices
# For each edge, store (to, capacity, reverse_edge_index)
graph = defaultdict(list)
def add_edge(u, v, cap):
# Forward edge
graph[u].append([v, cap, len(graph[v])])
# Reverse edge (capacity 0 initially)
graph[v].append([u, 0, len(graph[u]) - 1])
for _ in range(m):
a, b = map(int, input().split())
add_edge(a, b, 1)
def bfs():
"""Find augmenting path using BFS, return parent array"""
parent = {1: None} # node -> (prev_node, edge_index)
queue = deque([1])
while queue:
u = queue.popleft()
if u == n:
return parent
for i, (v, cap, _) in enumerate(graph[u]):
if cap > 0 and v not in parent:
parent[v] = (u, i)
queue.append(v)
return None
# Ford-Fulkerson with BFS (Edmonds-Karp)
max_flow = 0
while True:
parent = bfs()
if parent is None:
break
# Augment flow along path (always 1 for unit capacity)
max_flow += 1
# Update residual capacities
v = n
while parent[v] is not None:
u, edge_idx = parent[v]
rev_idx = graph[u][edge_idx][2]
# Decrease forward edge capacity
graph[u][edge_idx][1] -= 1
# Increase reverse edge capacity
graph[v][rev_idx][1] += 1
v = u
# Reconstruct paths
paths = []
for _ in range(max_flow):
path = [1]
curr = 1
while curr != n:
for i, (v, cap, rev_idx) in enumerate(graph[curr]):
# Check if this edge was used (reverse has capacity)
if graph[v][rev_idx][1] > 0 and cap == 0:
# Remove one unit of flow from this edge
graph[v][rev_idx][1] -= 1
path.append(v)
curr = v
break
paths.append(path)
# Output
print(max_flow)
for path in paths:
print(len(path), *path)
solve()
Wrong:
# Only tracking forward edges
if edge_used[u][v]:
continue
Correct:
# Must use residual graph with reverse edges
# Reverse edges allow "undoing" flow decisions
graph[v].append([u, 0, len(graph[u]) - 1]) # Reverse edge
Why it matters: Without reverse edges, the algorithm cannot correct suboptimal flow decisions and may find fewer paths than actually exist.
Wrong: Trying to reconstruct paths during the flow computation.
Correct: First compute maximum flow, then trace edges with flow from source to sink separately.
If there are multiple edges between the same pair of nodes, each must be treated as a separate edge with its own capacity.
CSES uses 1-indexed cities. Ensure your arrays are sized appropriately (MAXN = n+1).
For larger graphs or when better complexity is needed:
| Algorithm | Time Complexity | Notes |
|---|---|---|
| Edmonds-Karp | O(V * E^2) | BFS-based, used here |
| Dinic’s | O(V^2 * E) | Uses level graphs |
| Push-Relabel | O(V^2 * E) or O(V^3) | Different approach |
Dinic’s Algorithm is often preferred for competitive programming due to:
Time Complexity: O(V * E^2)
Space Complexity: O(V + E)
For this problem’s constraints (n <= 500, m <= 1000):