Part 07 Advanced Graphs | Previous: 24_Dijkstra | Next: 26_Minimum_Spanning_Tree
Introduction
The Floyd-Warshall algorithm is a dynamic programming (DP) based algorithm that finds the all-pairs shortest path.
It can handle negative weight edges and is also used for detecting negative cycles.
Applicable Graphs: Directed/Undirected, allows negative edges (when there are no negative cycles)
Time Complexity: O(V³)
Space Complexity: O(V²)
It is used when the number of vertices V is in the hundreds to thousands. Be careful of TLE if V > 500.
Learning Goals
Understand and implement the DP recurrence relation of Floyd-Warshall dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]).
Use the path recovery matrix (next[][]) to extract the actual path.
Apply Floyd-Warshall for negative cycle detection and reachability (transitive closure) calculation.
Key Concepts
1. DP Recurrence Relation
dist[i][j][k] = Shortest distance from i to j when only nodes 1~k can be visited
Recurrence formula:
dist[i][j][k] = min(
dist[i][j][k-1], ← Does not go through k
dist[i][k][k-1] + dist[k][j][k-1] ← Goes through k
)
Remove k dimension (space optimization):
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
2. Algorithm Structure
#Initialization
for i in range(V):
for j in range(V):
dist[i][j] = INF
dist[i][i] = 0 #Distance to self = 0
for u, v, w in edges:
dist[u][v] = min(dist[u][v], w)
#Main loop (k = intermediate node)
for k in range(V): #Order is important!
for i in range(V):
for j in range(V):
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
Note: In the three nested loops, k (intermediate node) should be the outermost loop.
3. Text Art: Example Graph and Step-by-Step Execution
k loop is the innermost: Incorrect result if k (intermediate node) is not the outermost loop.
Follow the order of for k → for i → for j.
INF + INF overflow: In dist[i][k] + dist[k][j], summing two INF values causes an error.
Add a guard in if dist[i][k] == INF or dist[k][j] == INF: continue or use float('inf').
Missing self-distance initialization: Forgetting dist[i][i] = 0 leads to an incorrect answer.
Path reconstruction matrix initialization error: Initialize with next[i][j] = j (direct connection) or next[i][j] = -1 (no connection),
then set to next[i][j] = j when adding edges.
Ignore V size limit: If V = 10,000, O(10¹²) operations → absolutely cannot use.
Use only when V ≤ 500 ~ 1,000.
Summary
Floyd-Warshall Summary
━━━━━━━━━━━━━━━━━━━━━
Problem Type : All Pairs Shortest Path
Time Complexity : O(V³)
Space Complexity : O(V²)
Negative Edges : Allowed (when no cycles)
Negative Cycles : Detected by dist[i][i] < 0
Path Reconstruction : next[V][V] matrix
Reachability : Applied using a bool matrix
Try It Yourself
Trace the text art example above by hand for k=0~3 steps and find the final dist.
Modify src/02_floyd_path.py to find the path with the fewest intermediate nodes.
Submit Baekjoon 11404 (Floyd) directly to get AC.
Example files
The runnable files used in this lesson. Copy them into your own project and experiment.
src/01_floyd_warshall.pyPython
"""Chapter 25 — Floyd-Warshall: Basic Implementation
Topic : Floyd-Warshall All-Pairs Shortest Path
Time : O(V³)
Space : O(V²)
Description:
Calculates all-pairs shortest paths using DP.
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
Note that k (the intermediate node) is in the outermost loop."""
import sys
INF = float("inf")
def floyd_warshall(
n: int,
edges: list[tuple[int, int, int]],
directed: bool = True,
) -> list[list[float]]:
"""Returns all-pairs shortest distances using Floyd-Warshall.
Args:
n : Number of nodes (0-indexed: 0 ~ n-1)
edges : List of edges as (u, v, w)
directed: True if the graph is directed
Returns:
dist[i][j]: Shortest distance from i to j (float('inf') if unreachable)
Time : O(V³)
Space: O(V²)"""
#Initialization: INF, itself is 0
dist: list[list[float]] = [[INF] * n for _ in range(n)]
for i in range(n):
dist[i][i] = 0.0
#Input edges
for u, v, w in edges:
dist[u][v] = min(dist[u][v], float(w))
if not directed:
dist[v][u] = min(dist[v][u], float(w))
#Main loop — k (intermediate node) is the outermost!
for k in range(n):
for i in range(n):
if dist[i][k] == INF: #Early termination optimization
continue
for j in range(n):
via_k = dist[i][k] + dist[k][j]
if via_k < dist[i][j]:
dist[i][j] = via_k
return dist
def print_dist_matrix(dist: list[list[float]], labels: list[str]) -> None:
"""Prints the distance matrix in a readable format."""
n = len(labels)
header = " " + "".join(f"{lb:>8}" for lb in labels)
print(header)
print(" " + "-" * (8 * n))
for i, row in enumerate(dist):
cells = []
for d in row:
cells.append("INF" if d == INF else f"{int(d)}")
print(f" {labels[i]} |" + "".join(f"{c:>8}" for c in cells))
def main() -> None:
#── Example 1: Lecture main graph ──
print("=" * 60)
print("Example 1: Directed graph (0~3)")
print("=" * 60)
edges1 = [
(0, 1, 3),
(0, 2, 2),
(0, 3, 7),
(1, 2, 1),
(2, 3, 1),
]
dist1 = floyd_warshall(4, edges1, directed=True)
print_dist_matrix(dist1, ["0", "1", "2", "3"])
print()
print("Direct check (shortest 0→3):", int(dist1[0][3]))
#── Example 2: Including negative edges ──
print()
print("=" * 60)
print("Example 2: Including negative edges (no cycles)")
print("=" * 60)
edges2 = [
(0, 1, 5),
(1, 2, -3),
(0, 2, 4),
(2, 3, 2),
]
dist2 = floyd_warshall(4, edges2, directed=True)
print_dist_matrix(dist2, ["A", "B", "C", "D"])
print()
print("Shortest A→C:", int(dist2[0][2]), "(5 + (-3) = 2 vs direct 4 → choose 2)")
#── Example 3: Undirected graph ──
print()
print("=" * 60)
print("Example 3: Undirected graph")
print("=" * 60)
edges3 = [
(0, 1, 1),
(1, 2, 3),
(0, 2, 10),
(2, 3, 2),
]
dist3 = floyd_warshall(4, edges3, directed=False)
print_dist_matrix(dist3, ["P", "Q", "R", "S"])
print()
print("Shortest P→S:", int(dist3[0][3]), "(P→Q→R→S = 1+3+2 = 6)")
#── Complexity summary ──
print()
print("=" * 60)
print("Complexity summary")
print("=" * 60)
for v in [10, 100, 500, 1000]:
ops = v ** 3
print(f"V={v:>5}: O(V³) = {ops:>12,} operations"
f"{'OK' if ops <= 1e8 else 'TLE risk'}")
if __name__ == "__main__":
main()
src/02_floyd_path.pyPython
"""Chapter 25 — Floyd-Warshall: Path Reconstruction (next matrix)
Topic : Floyd-Warshall with Path Reconstruction
Time : O(V³) construction / O(V) path reconstruction
Space : O(V²)
Description:
next[i][j] = the next node after i in the shortest path from i to j.
Initialization: for directly connected edges (u,v) → next[u][v] = v
Update: when dist[i][j] is updated, next[i][j] = next[i][k]
Reconstruction: starting from i and following next[cur][j] gives the path."""
INF = float("inf")
def floyd_warshall_with_path(
n: int,
edges: list[tuple[int, int, int]],
directed: bool = True,
) -> tuple[list[list[float]], list[list[int]]]:
"""Returns the Floyd-Warshall + path reconstruction matrices.
Returns:
(dist, nxt)
dist[i][j]: shortest distance from i to j
nxt[i][j]: next node after i on the shortest path to j (-1 if none)
Time : O(V³)
Space: O(V²)"""
dist: list[list[float]] = [[INF] * n for _ in range(n)]
nxt: list[list[int]] = [[-1] * n for _ in range(n)]
#Initialization
for i in range(n):
dist[i][i] = 0.0
for u, v, w in edges:
if float(w) < dist[u][v]:
dist[u][v] = float(w)
nxt[u][v] = v
if not directed and float(w) < dist[v][u]:
dist[v][u] = float(w)
nxt[v][u] = u
#Main Loop
for k in range(n):
for i in range(n):
if dist[i][k] == INF:
continue
for j in range(n):
via_k = dist[i][k] + dist[k][j]
if via_k < dist[i][j]:
dist[i][j] = via_k
nxt[i][j] = nxt[i][k] #← Update Path
return dist, nxt
def reconstruct_path(
nxt: list[list[int]],
src: int,
dst: int,
) -> list[int]:
"""Reconstructs the shortest path from src → dst using the nxt matrix.
Returns:
List of path nodes (empty list if unreachable)
Time : O(V)
Space: O(V)"""
if nxt[src][dst] == -1:
return []
path = [src]
cur = src
while cur != dst:
cur = nxt[cur][dst]
if cur == -1:
return [] #Interrupted in the middle (Abnormal)
path.append(cur)
return path
def main() -> None:
#── Example Graph ──
#Nodes: 0=Seoul, 1=Daejeon, 2=Daegu, 3=Busan
#Edges (Undirected)
labels = ["Seoul", "Daejeon", "Daegu", "Busan"]
edges = [
(0, 1, 140), #Seoul-Daejeon
(1, 2, 120), #Daejeon-Daegu
(2, 3, 80), #Daegu-Busan
(0, 3, 450), #Seoul-Busan (Direct)
(1, 3, 200), #Daejeon-Busan
]
n = 4
dist, nxt = floyd_warshall_with_path(n, edges, directed=False)
print("=" * 60)
print("Distance Matrix")
print("=" * 60)
header = " " + "".join(f"{lb:>8}" for lb in labels)
print(header)
for i, row in enumerate(dist):
cells = ["INF" if d == INF else str(int(d)) for d in row]
print(f" {labels[i]:>4} |" + "".join(f"{c:>8}" for c in cells))
print()
print("=" * 60)
print("Path Reconstruction")
print("=" * 60)
for src in range(n):
for dst in range(n):
if src == dst:
continue
path = reconstruct_path(nxt, src, dst)
if path:
p_str = " → ".join(labels[p] for p in path)
print(f" {labels[src]} → {labels[dst]}: "
f"Distance={int(dist[src][dst])}km Path={p_str}")
print()
print("=" * 60)
print("Seoul → Busan Details")
print("=" * 60)
path_sb = reconstruct_path(nxt, 0, 3)
p_str = " → ".join(labels[p] for p in path_sb)
print(f"Path: {p_str}")
print(f"Distance: {int(dist[0][3])} km")
print(f"Direct (450) vs Via ({int(dist[0][3])}) →"
f"{'Via is advantageous' if dist[0][3] < 450 else 'Direct is advantageous'}")
if __name__ == "__main__":
main()
src/03_negative_cycle.pyPython
"""Chapter 25 — Floyd-Warshall: Negative Cycle Detection
Topic : Negative Cycle Detection via Floyd-Warshall
Time : O(V³)
Space : O(V²)
Description:
After running Floyd-Warshall, if there exists a node with dist[i][i] < 0,
that node is on a negative cycle.
Additionally: if dist[i][j] ≠ INF but dist[i][i] < 0 or
dist[j][j] < 0, the shortest distance from i to j is -∞ (undefined)."""
INF = float("inf")
def floyd_warshall(
n: int,
edges: list[tuple[int, int, int]],
) -> list[list[float]]:
"""Basic Floyd-Warshall (directed graph). Time O(V³), Space O(V²)."""
dist: list[list[float]] = [[INF] * n for _ in range(n)]
for i in range(n):
dist[i][i] = 0.0
for u, v, w in edges:
dist[u][v] = min(dist[u][v], float(w))
for k in range(n):
for i in range(n):
if dist[i][k] == INF:
continue
for j in range(n):
via_k = dist[i][k] + dist[k][j]
if via_k < dist[i][j]:
dist[i][j] = via_k
return dist
def detect_negative_cycles(dist: list[list[float]]) -> list[int]:
"""Returns the list of nodes that belong to a negative cycle.
Principle: If there is a negative cycle, for the node i in it, dist[i][i] < 0.
Time : O(V)
Space: O(V)"""
return [i for i in range(len(dist)) if dist[i][i] < 0]
def is_reachable_through_neg_cycle(
dist: list[list[float]],
neg_nodes: list[int],
src: int,
dst: int,
) -> bool:
"""Checks whether the path from src → dst passes through a negative cycle.
If it does, the shortest distance is -∞.
Time : O(V)"""
for k in neg_nodes:
if dist[src][k] < INF and dist[k][dst] < INF:
return True
return False
def main() -> None:
#── Case 1: No negative cycle ──
print("=" * 60)
print("Case 1: There are negative edges but no cycle")
print(" 0→1: 2, 1→2: -1, 0→2: 4")
print("=" * 60)
edges1 = [(0, 1, 2), (1, 2, -1), (0, 2, 4)]
dist1 = floyd_warshall(3, edges1)
neg1 = detect_negative_cycles(dist1)
for i in range(3):
print(f" dist[{i}][{i}] = {dist1[i][i]}")
print(f"Negative cycle nodes: {neg1 if neg1 else 'None'}")
print(f"0→2 shortest: {int(dist1[0][2])} (0→1→2 = 2-1 = 1 < 4)")
#── Case 2: Negative cycle exists ──
print()
print("=" * 60)
print("Case 2: Negative cycle exists")
print("0→1: 1, 1→2: -3, 2→1: 1 (cycle 1→2→1 = -3+1 = -2)")
print("=" * 60)
edges2 = [(0, 1, 1), (1, 2, -3), (2, 1, 1), (2, 3, 1)]
dist2 = floyd_warshall(4, edges2)
neg2 = detect_negative_cycles(dist2)
for i in range(4):
d = dist2[i][i]
print(f"dist[{i}][{i}] = {d:.1f} {'← Negative cycle!' if d < 0 else ''}")
print(f"Negative cycle nodes: {neg2}")
print(f"Does the 0→3 path pass through a negative cycle?:"
f"{is_reachable_through_neg_cycle(dist2, neg2, 0, 3)}")
print("→ 0→3 shortest distance is -∞ (does not converge)")
#── Case 3: Practical use — branching depending on the presence of a negative cycle ──
print()
print("=" * 60)
print("Case 3: Practical code pattern")
print("=" * 60)
edges3 = [(0, 1, 3), (1, 2, 2), (2, 0, -4)] #0→1→2→0 = 1 (negative cycle)
dist3 = floyd_warshall(3, edges3)
neg3 = detect_negative_cycles(dist3)
if neg3:
print(f"Warning: Negative cycle detected (nodes {neg3})")
print("Shortest path undefined — cycle handling required using Bellman-Ford, etc.")
else:
print("No negative cycle — Floyd-Warshall results are reliable")
#── Summary ──
print()
print("=" * 60)
print("Summary of negative cycle detection rules")
print("=" * 60)
print("After running Floyd-Warshall:")
print("dist[i][i] < 0 → Node i is on a negative cycle")
print("If paths i→k and k→j both exist and k is on a negative cycle")
print("→ i→j shortest = -∞")
if __name__ == "__main__":
main()
src/04_transitive_closure.pyPython
"""Unit 25 — Floyd-Warshall: Transitive Closure
Topic: Transitive Closure (Reachability) via Floyd-Warshall
Time: O(V³) — can be optimized to O(V³/64) using bit operations
Space: O(V²)
Description:
If reach[i][j] = True, j is reachable from i.
Replace min(dist) in Floyd-Warshall with OR(reach).
reach[i][j] |= (reach[i][k] and reach[k][j])
Or using integer bitmask: reach[i] |= reach[k] if reach[i] >> k & 1"""
def transitive_closure_bool(
n: int,
edges: list[tuple[int, int]],
directed: bool = True,
) -> list[list[bool]]:
"""Compute transitive closure using a boolean matrix.
Time: O(V³)
Space: O(V²)"""
reach: list[list[bool]] = [[False] * n for _ in range(n)]
#Reachable by oneself
for i in range(n):
reach[i][i] = True
#Directly connected edge
for u, v in edges:
reach[u][v] = True
if not directed:
reach[v][u] = True
#Floyd-Warshall (OR version)
for k in range(n):
for i in range(n):
if not reach[i][k]:
continue
for j in range(n):
if reach[k][j]:
reach[i][j] = True
return reach
def transitive_closure_bitset(
n: int,
edges: list[tuple[int, int]],
directed: bool = True,
) -> list[int]:
"""Compute transitive closure using integer bitmasks.
If the j-th bit of reach[i] is 1, i→j is reachable.
Time: O(V³ / 64) (using integer bit operations)
Space: O(V²/64) → effectively an O(V) list of integers"""
reach = [0] * n
#Oneself
for i in range(n):
reach[i] |= (1 << i)
#Directly connected
for u, v in edges:
reach[u] |= (1 << v)
if not directed:
reach[v] |= (1 << u)
#Floyd (bit OR)
for k in range(n):
for i in range(n):
if reach[i] >> k & 1: #Reachable i → k
reach[i] |= reach[k] #All neighbors of i → k reachable
return reach
def print_reach_matrix(reach: list[list[bool]], labels: list[str]) -> None:
"""Output the reachability matrix."""
n = len(labels)
header = " " + "".join(f"{lb:>5}" for lb in labels)
print(header)
print(" " + "─" * (5 * n))
for i, row in enumerate(reach):
cells = ["O" if r else "X" for r in row]
print(f" {labels[i]:>3} |" + "".join(f"{c:>5}" for c in cells))
def main() -> None:
#── Example 1: Directed Graph ──
print("=" * 55)
print("Example 1: Directed Graph Transitive Closure")
print("0→1, 1→2, 2→3, 3→0 (Cycle)")
print("=" * 55)
edges1 = [(0, 1), (1, 2), (2, 3), (3, 0)]
n = 4
labels = ["0", "1", "2", "3"]
reach_bool = transitive_closure_bool(n, edges1)
print_reach_matrix(reach_bool, labels)
reach_bits = transitive_closure_bitset(n, edges1)
print()
print("Bitmask result (same content):")
for i in range(n):
reachable = [j for j in range(n) if reach_bits[i] >> j & 1]
print(f"Reachable from {i}: {reachable}")
#── Example 2: DAG (No cycles) ──
print()
print("=" * 55)
print("Example 2: DAG — Course prerequisite relationships")
print("Math→Physics→Chemistry, Math→Statistics→AI, Physics→AI")
print("=" * 55)
subjects = ["Mathematics", "Physics", "Chemistry", "Statistics", "AI"]
edges2 = [(0, 1), (1, 2), (0, 3), (3, 4), (1, 4)]
reach2 = transitive_closure_bool(5, edges2)
print_reach_matrix(reach2, ["Mathematics", "Physics", "Chemistry", "Statistics", "AI"])
print()
print("Courses available if you know 'Math':")
can_take = [subjects[j] for j in range(5) if reach2[0][j] and j != 0]
print(f" {can_take}")
#── Example 3: Baekjoon 11403 type ──
print()
print("=" * 55)
print("Example 3: Baekjoon 11403 style — 0-indexed adjacency matrix input")
print("=" * 55)
adj = [
[0, 1, 0, 0],
[0, 0, 0, 1],
[0, 0, 0, 0],
[0, 0, 0, 0],
]
edges3 = [(i, j) for i in range(4) for j in range(4) if adj[i][j]]
reach3 = transitive_closure_bool(4, edges3)
print("Input adj:")
for row in adj:
print(f" {row}")
print("Output reach (O=1, X=0):")
for row in reach3:
print(f" {[1 if r else 0 for r in row]}")
if __name__ == "__main__":
main()
Assignment
Solve it directly on Baekjoon Online Judge first, then check the reference solution below.
n cities, m buses. Find the minimum cost bus for every pair of cities (i, j).
If not reachable, output 0.
Input
n (number of cities, 1 ≤ n ≤ 100)
m (number of buses, 1 ≤ m ≤ 100,000)
a b c (cost c from a→b)
...
Constraints
1 ≤ n ≤ 100 → V³ = 10⁶ is sufficient
There can be multiple buses for the same (a, b) → choose the minimum value
Solution Strategy
Basic Floyd-Warshall
When outputting, if INF, print 0
Solution Files
boj_11404.py
Problem 2: Baekjoon 1389 — Six Degrees of Kevin Bacon (Silver I)
Problem Summary
N people, M relationships. Kevin Bacon number = sum of shortest distances to all other people.
Print the person number with the smallest Kevin Bacon number.
Input
N M (number of people, number of relationships)
a b (a-b friends)
...
Constraints
1 ≤ N ≤ 100, 1 ≤ M ≤ 5,050
Solution Strategy
Undirected Floyd-Warshall
Calculate the sum of distances from each node to all other nodes
Output the node with the minimum value
Solution Files
boj_1389.py
Problem 3: Baekjoon 11403 — Find a Path (Silver I)
Problem Summary
Unweighted directed graph. Output a matrix with 1 if there is a path from i to j, 0 if not.
Input
N (Number of nodes, 1 ≤ N ≤ 100)
(N×N adjacency matrix)
Solution Strategy
Floyd-Warshall OR version (transitive closure)
reach[i][j] |= reach[i][k] and reach[k][j]
Self (i==j): Output 0 based on the input matrix
Solution Files
boj_11403.py
Pre-submission Checklist
k loop is outermost: for k → for i → for j
Initialize dist[i][i] = 0
Multiple same edges: dist[u][v] = min(dist[u][v], w)
Prevent INF + w overflow: float('inf') or if dist[i][k] == INF: continue
Output format: unreachable representation differs for each problem (0 or INF)
11403: Self (i==j) ignores input matrix value and is handled separately
Show the reference solution
Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.
boj_11403.pyPython
"""Baekjoon 11403 — Finding Paths
https://www.acmicpc.net/problem/11403
Difficulty: Silver I
Time: O(V³) V ≤ 100
Space: O(V²)
Solution:
Transitive Closure problem.
Floyd-Warshall OR version: reach[i][j] |= reach[i][k] and reach[k][j]
Ignore input for i==j, assume 0 (paths to oneself are not asked in the problem).
Note: The initial value for oneself (i==j) should also be 0, so do not initialize dist[i][i] = 0."""
import sys
input = sys.stdin.readline
def solve() -> None:
N = int(input())
adj = []
for _ in range(N):
row = list(map(int, input().split()))
adj.append(row)
#Transition closure (0-indexed, i==j remains 0)
reach = [row[:] for row in adj] #Direct connection copy
for k in range(N):
for i in range(N):
if not reach[i][k]:
continue
for j in range(N):
if reach[k][j]:
reach[i][j] = 1
#Output
result = []
for i in range(N):
result.append(" ".join(map(str, reach[i])))
print("\n".join(result))
if __name__ == "__main__":
solve()
boj_11404.pyPython
"""Baekjoon 11404 — Floyd
https://www.acmicpc.net/problem/11404
Difficulty: Gold IV
Time : O(V³) V ≤ 100
Space : O(V²)
Solution:
All pairs shortest path → Floyd-Warshall.
Multiple buses for the same (a, b) → initialize with the minimum value.
If unreachable → print 0."""
import sys
input = sys.stdin.readline
INF = float("inf")
def solve() -> None:
n = int(input())
m = int(input())
dist = [[INF] * (n + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
dist[i][i] = 0.0
for _ in range(m):
a, b, c = map(int, input().split())
dist[a][b] = min(dist[a][b], float(c))
#Floyd-Warshall
for k in range(1, n + 1):
for i in range(1, n + 1):
if dist[i][k] == INF:
continue
for j in range(1, n + 1):
via = dist[i][k] + dist[k][j]
if via < dist[i][j]:
dist[i][j] = via
#Output (unreachable → 0)
result = []
for i in range(1, n + 1):
row = []
for j in range(1, n + 1):
row.append("0" if dist[i][j] == INF else str(int(dist[i][j])))
result.append(" ".join(row))
print("\n".join(result))
if __name__ == "__main__":
solve()
boj_1389.pyPython
"""Baekjoon 1389 — Kevin Bacon's Six Degrees of Separation
https://www.acmicpc.net/problem/1389
Difficulty: Silver I
Time: O(V³) V ≤ 100
Space: O(V²)
Solution:
Undirected graph Floyd-Warshall.
Kevin Bacon number of each person i = sum(dist[i][j] for j != i).
Print the person number with the smallest Kevin Bacon number (if tied, the smaller number)."""
import sys
input = sys.stdin.readline
INF = float("inf")
def solve() -> None:
N, M = map(int, input().split())
dist = [[INF] * (N + 1) for _ in range(N + 1)]
for i in range(1, N + 1):
dist[i][i] = 0.0
for _ in range(M):
a, b = map(int, input().split())
dist[a][b] = 1.0
dist[b][a] = 1.0
#Floyd-Warshall
for k in range(1, N + 1):
for i in range(1, N + 1):
if dist[i][k] == INF:
continue
for j in range(1, N + 1):
via = dist[i][k] + dist[k][j]
if via < dist[i][j]:
dist[i][j] = via
#Kevin Bacon number calculation
best_person = 1
best_score = sum(dist[1][j] for j in range(1, N + 1) if j != 1)
for i in range(2, N + 1):
score = sum(dist[i][j] for j in range(1, N + 1) if j != i)
if score < best_score:
best_score = score
best_person = i
print(best_person)
if __name__ == "__main__":
solve()