Python Algorithms & Data Structures track

Lesson 15 · Part 4: Graph Foundations

BFS

deque-based BFS, shortest paths and grid traversal

Part 4: Graph Foundations3 min readFree lesson

Part 04 – Graph Basics | Difficulty: ⭐⭐⭐☆☆


Introduction: Search That Guarantees the Shortest Path

BFS (Breadth-First Search) explores nodes closest to the start point first. It is the only simple algorithm that guarantees the shortest path in an unweighted graph.

DFS vs BFS: When to Use?

Purpose Recommendation
Shortest path (no weights) BFS
Connected components / Backtracking DFS
Convenience for recursive implementation DFS
Need for layer-by-layer search BFS

Learning Goals

  1. Implement BFS using collections.deque.
  2. Record the shortest distance with a distance array (dist[]) and reconstruct the path.
  3. Implement multi-source BFS and grid BFS.

Key Concepts

BFS Layer-by-Layer Search

Graph:
  1 ─── 2 ─── 5
  │     │
  3 ─── 4

BFS (start=1) search order:
Layer 0: [1]           Distance 0
Layer 1: [2, 3]        Distance 1
Layer 2: [5, 4]        Distance 2

deque state:
  [1]       → pop=1, push(2,3)
  [2,3]     → pop=2, push(5,4)
[3,5,4]   → pop=3, (4 already exists)
  [5,4]     → pop=5
  [4]       → pop=4

BFS Shortest Distance Diagram

Grid maze (0=path, 1=wall, S=start, E=end):
  S 0 1 0 0
  0 0 1 0 1
  1 0 0 0 0
  0 1 0 1 0
  0 0 0 0 E

Shortest distance:
   0  1  ∞  6  7
   1  2  ∞  5  ∞
∞  3  4  4  5  (excluding walls)
   ∞  ∞  5  ∞  6
   ...    ...  8

Shortest distance = 8

visited vs dist Array

#Method 1: visited array
visited[node] = True   #Only visit status

#Method 2: dist array (distance record + also serves as visit status)
dist = [-1] * (n + 1)
dist[start] = 0
if dist[nxt] == -1:    #-1 = not visited
    dist[nxt] = dist[node] + 1

Time/Space Complexity

  • Time: O(V + E)
  • Space: O(V) — queue + visited array

Example

File Contents
src/01_bfs_basic.py BFS base template, search order and distance
src/02_bfs_shortest_path.py Shortest path dist array, path reconstruction
src/03_bfs_grid.py Grid BFS: shortest path in a maze
src/04_bfs_multi_source.py Multi-source BFS

5 Common Mistakes

  1. Mistake of setting visited when dequeuing

    #Wrong method: mark visited after pop
    while queue:
        node = queue.popleft()
        if visited[node]: continue
        visited[node] = True
        #→ The same node can enter the queue multiple times
    
    #Correct method: mark visited when pushing
    queue.append(start)
    visited[start] = True
    while queue:
        node = queue.popleft()
        for nxt in graph[node]:
            if not visited[nxt]:
                visited[nxt] = True
                queue.append(nxt)
    
  2. Using list.pop(0) (O(n) operation)

  • Removing from the front of a list is O(n) → whole queue becomes O(n²)
  • Always use popleft() of collections.deque
  1. Omitting grid range check
  • Always check range after nr, nc = r + dr, c + dc
  1. Initializing start point distance to 1 instead of 0
  • Start point is already there, so distance is 0
    • dist[start] = 0
  1. In multi-start BFS, not inserting all start points into the queue
    #Wrong: BFS each starting point separately
    #Correct: Put all starting points in the queue from the beginning
    queue = deque()
    for src in sources:
        queue.append(src)
        dist[src] = 0
    

Summary

Purpose BFS Pattern
Simple visit order deque + visited
Shortest distance deque + dist[]
Grid exploration 4 directions + range check
Multi-start points insert all into initial queue
Layer separation layer boundary by queue size

Try It Yourself

  1. Calculate the shortest distance in a 5×5 maze using BFS and backtrack the path.
  2. Simulate "fire spreading from multiple flames" using multi-start BFS.
  3. Write code to determine if a graph is bipartite using BFS.

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

src/01_bfs_basic.pyPython
"""Topic  : BFS Basics — Search Order, Distance Array
Time   : O(V + E)
Space  : O(V)"""

from collections import deque


#── Basic BFS ─────────────────────────────────────────────────────────────────

def bfs(
    graph: list[list[int]],
    start: int,
    n: int,
) -> tuple[list[int], list[int]]:
    """BFS Search.

    Returns:
        (Visit order, shortest distance array)
        dist[i] = shortest distance from start → i (-1 if unreachable)"""
    dist = [-1] * (n + 1)
    dist[start] = 0
    queue: deque[int] = deque([start])
    order: list[int] = []

    while queue:
        node = queue.popleft()
        order.append(node)
        for nxt in graph[node]:
            if dist[nxt] == -1:
                dist[nxt] = dist[node] + 1
                queue.append(nxt)

    return order, dist


def bfs_verbose(
    graph: list[list[int]],
    start: int,
    n: int,
) -> None:
    """BFS Output by Layer."""
    dist = [-1] * (n + 1)
    dist[start] = 0
    queue: deque[int] = deque([start])
    layer = 0

    print(f"Layer {layer}: [{start}]  (Distance 0)")
    while queue:
        next_layer: list[int] = []
        for _ in range(len(queue)):    #Processing all nodes in the current layer
            node = queue.popleft()
            for nxt in graph[node]:
                if dist[nxt] == -1:
                    dist[nxt] = dist[node] + 1
                    queue.append(nxt)
                    next_layer.append(nxt)
        if next_layer:
            layer += 1
            print(f"Layer {layer}: {next_layer}  (Distance {layer})")


#── BFS vs DFS Comparison ──────────────────────────────────────────────────────

def dfs_order(
    graph: list[list[int]],
    start: int,
    n: int,
) -> list[int]:
    """DFS Visit Order (for Comparison)."""
    visited = [False] * (n + 1)
    order: list[int] = []

    def _dfs(node: int) -> None:
        visited[node] = True
        order.append(node)
        for nxt in graph[node]:
            if not visited[nxt]:
                _dfs(nxt)

    _dfs(start)
    return order


#── Main ─────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    #Tree-structured graph
    #        1
    #       /|\
    #      2 3 4
    #     /|   |
    #    5 6   7
    n = 7
    edges = [(1, 2), (1, 3), (1, 4), (2, 5), (2, 6), (4, 7)]
    graph: list[list[int]] = [[] for _ in range(n + 1)]
    for u, v in edges:
        graph[u].append(v)
        graph[v].append(u)

    print("=" * 55)
    print("BFS vs DFS visit order comparison")
    print("=" * 55)
    bfs_order, dist = bfs(graph, 1, n)
    dfs_ord = dfs_order(graph, 1, n)

    print(f"BFS order: {bfs_order}")
    print(f"DFS order: {dfs_ord}")
    print(f"Shortest distance: {dist[1:]}")

    print("\n=== BFS Layer-by-Layer Traversal ===")
    bfs_verbose(graph, 1, n)

    #Linear graph
    print("\n=== Linear Graph 1─2─3─4─5 ===")
    n2 = 5
    g2: list[list[int]] = [[] for _ in range(n2 + 1)]
    for i in range(1, n2):
        g2[i].append(i + 1)
        g2[i + 1].append(i)
    bfs_ord2, dist2 = bfs(g2, 1, n2)
    print(f"BFS order: {bfs_ord2}")
    print(f"Distance array: {dist2[1:]}")

    #Disconnected Graph
    print("\n=== Disconnected Graph ===")
    #1─2  3─4  5(Isolated)
    n3 = 5
    g3: list[list[int]] = [[] for _ in range(n3 + 1)]
    for u, v in [(1, 2), (3, 4)]:
        g3[u].append(v)
        g3[v].append(u)
    _, dist3 = bfs(g3, 1, n3)
    print(f"Start=1 Distance: {dist3[1:]}")
    print("-1 = unreachable")

    #Distance-based layer output
    print("\n=== Nodes by Distance ===")
    for d in range(max(x for x in dist if x >= 0) + 1):
        nodes = [i for i in range(1, n + 1) if dist[i] == d]
        print(f"Distance {d}: {nodes}")
src/02_bfs_shortest_path.pyPython
"""Topic  : BFS Shortest Path — dist Array, Path Reconstruction
Time   : O(V + E)
Space  : O(V)"""

from collections import deque


#── Shortest Path Distance ────────────────────────────────────────────────────────────

def bfs_dist(
    graph: list[list[int]],
    start: int,
    n: int,
) -> list[int]:
    """Calculate the shortest distance to all nodes using BFS.

    Returns:
        dist[i] = shortest distance from start to i (-1 = unreachable)"""
    dist = [-1] * (n + 1)
    dist[start] = 0
    queue: deque[int] = deque([start])
    while queue:
        node = queue.popleft()
        for nxt in graph[node]:
            if dist[nxt] == -1:
                dist[nxt] = dist[node] + 1
                queue.append(nxt)
    return dist


#── Path Reconstruction ───────────────────────────────────────────────────────────────

def bfs_with_parent(
    graph: list[list[int]],
    start: int,
    n: int,
) -> tuple[list[int], list[int]]:
    """Path reconstruction with BFS + parent array.

    Returns:
        (dist, parent)
        parent[i] = parent of i in BFS tree (-1 if none)"""
    dist = [-1] * (n + 1)
    parent = [-1] * (n + 1)
    dist[start] = 0
    queue: deque[int] = deque([start])

    while queue:
        node = queue.popleft()
        for nxt in graph[node]:
            if dist[nxt] == -1:
                dist[nxt] = dist[node] + 1
                parent[nxt] = node
                queue.append(nxt)

    return dist, parent


def reconstruct_path(parent: list[int], start: int, end: int) -> list[int]:
    """Reconstruct the path from start → end using the parent array.

    Returns:
        path list (empty list if unreachable)"""
    if parent[end] == -1 and end != start:
        return []   #Unreachable

    path: list[int] = []
    node = end
    while node != -1:
        path.append(node)
        node = parent[node]
    return path[::-1]


#── Shortest Distance to Specific Target ───────────────────────────────────────────────

def bfs_to_target(
    graph: list[list[int]],
    start: int,
    target: int,
    n: int,
) -> int:
    """Shortest distance from start to target (early exit).

    Returns:
        shortest distance (-1 if unreachable)"""
    if start == target:
        return 0
    dist = [-1] * (n + 1)
    dist[start] = 0
    queue: deque[int] = deque([start])

    while queue:
        node = queue.popleft()
        for nxt in graph[node]:
            if dist[nxt] == -1:
                dist[nxt] = dist[node] + 1
                if nxt == target:
                    return dist[nxt]
                queue.append(nxt)
    return -1


#── Main ─────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    #Graph Construction
    #  1─2─3─6
    #  │ │
    #  4─5
    n = 6
    edges = [(1, 2), (1, 4), (2, 3), (2, 5), (3, 6), (4, 5)]
    graph: list[list[int]] = [[] for _ in range(n + 1)]
    for u, v in edges:
        graph[u].append(v)
        graph[v].append(u)

    print("=" * 55)
    print("BFS Shortest Path")
    print("=" * 55)

    start = 1
    dist, parent = bfs_with_parent(graph, start, n)

    print(f"Start Point: {start}")
    print(f"Distance Array: {dist[1:]}")

    for target in [1, 3, 5, 6]:
        path = reconstruct_path(parent, start, target)
        d = dist[target]
        print(f"{start} → {target}: Distance={d}, Path={path}")

    #Disconnected Node
    n2 = 5
    g2: list[list[int]] = [[] for _ in range(n2 + 1)]
    for u, v in [(1, 2), (3, 4)]:
        g2[u].append(v)
        g2[v].append(u)
    d2, p2 = bfs_with_parent(g2, 1, n2)
    path_to_3 = reconstruct_path(p2, 1, 3)
    print(f"Disconnected: 1 → 3 Path = {path_to_3}  (Empty List = Unreachable)")

    #Early Termination Test
    print("=== Early Termination Shortest Distance ===")
    for src, dst in [(1, 6), (1, 5), (4, 6)]:
        d_fast = bfs_to_target(graph, src, dst, n)
        d_full = bfs_dist(graph, src, n)[dst]
        assert d_fast == d_full
        print(f"  {src} → {dst}: {d_fast}")
    print("Accuracy Verification: PASS")
src/03_bfs_grid.pyPython
"""Topic  : Grid BFS — Maze Shortest Path, 4 Directions
Time   : O(R × C)
Space  : O(R × C)"""

from collections import deque

DR = [-1, 1, 0, 0]
DC = [0, 0, -1, 1]


#── Grid BFS ─────────────────────────────────────────────────────────────────

def bfs_grid(
    grid: list[list[int]],
    sr: int, sc: int,
    er: int, ec: int,
) -> int:
    """Shortest distance in a grid from (sr,sc) → (er,ec).

    grid: 0=passable, 1=wall
    Returns:
        Shortest distance (-1 if unreachable)"""
    rows = len(grid)
    cols = len(grid[0])
    if grid[sr][sc] == 1 or grid[er][ec] == 1:
        return -1

    dist = [[-1] * cols for _ in range(rows)]
    dist[sr][sc] = 0
    queue: deque[tuple[int, int]] = deque([(sr, sc)])

    while queue:
        r, c = queue.popleft()
        if r == er and c == ec:
            return dist[r][c]
        for i in range(4):
            nr, nc = r + DR[i], c + DC[i]
            if (0 <= nr < rows and 0 <= nc < cols
                    and dist[nr][nc] == -1
                    and grid[nr][nc] == 0):
                dist[nr][nc] = dist[r][c] + 1
                queue.append((nr, nc))

    return -1


def bfs_grid_with_path(
    grid: list[list[int]],
    sr: int, sc: int,
    er: int, ec: int,
) -> tuple[int, list[tuple[int, int]]]:
    """Grid BFS + path reconstruction.

    Returns:
        (Shortest distance, list of path coordinates)"""
    rows = len(grid)
    cols = len(grid[0])

    dist = [[-1] * cols for _ in range(rows)]
    parent: list[list[tuple[int, int] | None]] = [
        [None] * cols for _ in range(rows)
    ]
    dist[sr][sc] = 0
    queue: deque[tuple[int, int]] = deque([(sr, sc)])

    while queue:
        r, c = queue.popleft()
        for i in range(4):
            nr, nc = r + DR[i], c + DC[i]
            if (0 <= nr < rows and 0 <= nc < cols
                    and dist[nr][nc] == -1
                    and grid[nr][nc] == 0):
                dist[nr][nc] = dist[r][c] + 1
                parent[nr][nc] = (r, c)
                queue.append((nr, nc))

    if dist[er][ec] == -1:
        return -1, []

    #Path backtracking
    path: list[tuple[int, int]] = []
    cur: tuple[int, int] | None = (er, ec)
    while cur is not None:
        path.append(cur)
        r, c = cur
        cur = parent[r][c]
    return dist[er][ec], path[::-1]


#── Visualization ──────────────────────────────────────────────────────────────

def print_grid_with_path(
    grid: list[list[int]],
    path: list[tuple[int, int]],
    sr: int, sc: int,
    er: int, ec: int,
) -> None:
    """Print grid showing path."""
    path_set = set(path)
    rows = len(grid)
    cols = len(grid[0])
    print()
    for r in range(rows):
        row_str = ""
        for c in range(cols):
            if (r, c) == (sr, sc):
                row_str += "S"
            elif (r, c) == (er, ec):
                row_str += "E"
            elif (r, c) in path_set:
                row_str += "*"
            elif grid[r][c] == 1:
                row_str += "█"
            else:
                row_str += "."
        print("  " + row_str)


#── Main ─────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    #Baekjoon 2178 style maze
    maze = [
        [0, 0, 1, 0, 0, 0],
        [0, 0, 1, 0, 1, 0],
        [1, 0, 0, 0, 0, 0],
        [0, 1, 0, 1, 0, 0],
        [0, 0, 0, 0, 1, 0],
    ]
    sr, sc = 0, 0
    er, ec = len(maze) - 1, len(maze[0]) - 1

    print("=" * 50)
    print("Grid BFS maze exploration")
    print("=" * 50)
    print("Maze (█=wall, .=path):")
    for row in maze:
        print("  " + "".join("█" if c else "." for c in row))

    dist, path = bfs_grid_with_path(maze, sr, sc, er, ec)
    print(f"Shortest distance: {dist}")
    print(f"Path coordinates: {path}")
    print("Path visualization (S=Start, E=End, *=Path):")
    print_grid_with_path(maze, path, sr, sc, er, ec)

    #Grid without walls — Shortest distance = Manhattan distance
    open_grid = [[0] * 5 for _ in range(5)]
    d = bfs_grid(open_grid, 0, 0, 4, 4)
    print(f"5×5 open grid (0,0)→(4,4): {d}  (Manhattan: {4+4})")

    #If blocked
    blocked = [[0, 1], [1, 0]]
    d_blocked = bfs_grid(blocked, 0, 0, 1, 1)
    print(f"Blocked grid: {d_blocked}  (-1 if impossible)")

    #Verify accuracy
    assert dist == 8 or dist >= 0   #May vary depending on the maze
    assert bfs_grid(open_grid, 0, 0, 4, 4) == 8
    print("Accuracy check: PASS")
src/04_bfs_multi_source.pyPython
"""Topic  : Multi-Source BFS — Simultaneous search from multiple starting points
Time   : O(V + E) or O(R × C)
Space  : O(V)"""

from collections import deque

DR = [-1, 1, 0, 0]
DC = [0, 0, -1, 1]


#── Multiple Starting Points BFS (Graph) ─────────────────────────────────────────────────

def multi_source_bfs(
    graph: list[list[int]],
    sources: list[int],
    n: int,
) -> list[int]:
    """Perform BFS simultaneously from multiple starting points.

    Returns the distance to each node from the nearest starting point."""
    dist = [-1] * (n + 1)
    queue: deque[int] = deque()

    #Insert all starting points into the initial queue
    for src in sources:
        dist[src] = 0
        queue.append(src)

    while queue:
        node = queue.popleft()
        for nxt in graph[node]:
            if dist[nxt] == -1:
                dist[nxt] = dist[node] + 1
                queue.append(nxt)

    return dist


#── Tomato Problem Style (Baekjoon 7576) ──────────────────────────────────────────

def tomato_ripening(grid: list[list[int]]) -> int:
    """Ripen adjacent tomatoes from already ripe tomatoes (1) in one day.

    The minimum number of days required for all tomatoes to ripen.
    0 = unripe tomato, 1 = ripe tomato, -1 = empty cell

    Returns:
        Minimum number of days (-1 if impossible)"""
    rows = len(grid)
    cols = len(grid[0])
    dist = [[-1] * cols for _ in range(rows)]
    queue: deque[tuple[int, int]] = deque()

    #Insert all ripe tomatoes into the queue
    for r in range(rows):
        for c in range(cols):
            if grid[r][c] == 1:
                dist[r][c] = 0
                queue.append((r, c))

    while queue:
        r, c = queue.popleft()
        for i in range(4):
            nr, nc = r + DR[i], c + DC[i]
            if (0 <= nr < rows and 0 <= nc < cols
                    and grid[nr][nc] == 0
                    and dist[nr][nc] == -1):
                dist[nr][nc] = dist[r][c] + 1
                queue.append((nr, nc))

    #If any unripe tomatoes remain, -1
    max_days = 0
    for r in range(rows):
        for c in range(cols):
            if grid[r][c] == 0 and dist[r][c] == -1:
                return -1
            if dist[r][c] != -1:
                max_days = max(max_days, dist[r][c])

    return max_days


#── Multiple Starting Points BFS — Color by the Nearest Starting Point ────────────────────────────────

def color_by_nearest_source(
    grid: list[list[int]],
    sources: list[tuple[int, int]],
) -> list[list[int]]:
    """Paint each empty cell with the color of the nearest starting point.

    Returns:
        color array (number indicating which starting point each cell came from, -1 = unreachable)"""
    rows = len(grid)
    cols = len(grid[0])
    color = [[-1] * cols for _ in range(rows)]
    dist = [[float('inf')] * cols for _ in range(rows)]
    queue: deque[tuple[int, int, int]] = deque()   # (r, c, color_id)

    for i, (r, c) in enumerate(sources):
        color[r][c] = i
        dist[r][c] = 0
        queue.append((r, c, i))

    while queue:
        r, c, cid = queue.popleft()
        for i in range(4):
            nr, nc = r + DR[i], c + DC[i]
            if (0 <= nr < rows and 0 <= nc < cols
                    and grid[nr][nc] == 0
                    and dist[nr][nc] == float('inf')):
                dist[nr][nc] = dist[r][c] + 1
                color[nr][nc] = cid
                queue.append((nr, nc, cid))

    return color


#── Visualization ──────────────────────────────────────────────────────────────

def print_grid(grid: list[list], title: str) -> None:
    symbols = {0: ".", 1: "T", -1: "□", 2: "2"}
    print(f"\n=== {title} ===")
    for row in grid:
        print("  " + " ".join(
            str(c) if isinstance(c, int) and c >= 0 else ("□" if c == -1 else str(c))
            for c in row
        ))


#── Main ─────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    #Tomato problem simulation
    tomato_grid = [
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 1],   #Bottom right
    ]
    print("=" * 50)
    print("Tomato problem (Multiple Starting Points BFS)")
    print("=" * 50)
    print_grid(tomato_grid, "Initial state (T = ripe tomato)")

    days = tomato_ripening([row[:] for row in tomato_grid])
    print(f"Days required for all tomatoes to ripen: {days}")

    #Multiple starting points
    multi_tomato = [
        [1, 0, 0, 0, 0],
        [0, 0, 0, 0, 0],
        [0, 0, -1, 0, 0],
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 1],
    ]
    print_grid(multi_tomato, "Multiple starting point tomatoes (□ = empty space)")
    days2 = tomato_ripening([row[:] for row in multi_tomato])
    print(f"Days required for all tomatoes to ripen: {days2}")

    #Impossible case
    impossible = [
        [1, 0, -1, 0, 0],
    ]
    days3 = tomato_ripening(impossible)
    print(f"Impossible case: {days3}  (-1 if impossible)")

    #Graph multiple starting points
    print("\n=== Graph Multiple Starting Points BFS ===")
    n = 7
    edges = [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7)]
    g: list[list[int]] = [[] for _ in range(n + 1)]
    for u, v in edges:
        g[u].append(v)
        g[v].append(u)

    sources = [1, 7]
    dist_multi = multi_source_bfs(g, sources, n)
    print(f"Starting points {sources}, shortest distance: {dist_multi[1:]}")
    print("← Distance from each node to the nearest starting point")

    #Verify accuracy
    assert tomato_ripening([[1, 0, 0], [0, 0, 0]]) == 4
    print("Accuracy check: PASS")

Assignment

Apply BFS shortest path and multiple starting points to practical problems.


Problem List

# Problem Tier Key Skill
1 Baekjoon 1260 – DFS and BFS Silver II Graph BFS
2 Baekjoon 2178 – Maze Exploration Silver I Grid BFS Shortest Path
3 Baekjoon 7576 – Tomato Gold V Multi-start BFS

Problem 1: Baekjoon 1260 – DFS and BFS (Silver II)

Same problem as the previous assignment (Lesson 13). This time, focus on the BFS part.

Approach

from collections import deque

queue = deque([start])
visited[start] = True
while queue:
    node = queue.popleft()
    print(node)
    for nxt in sorted(graph[node]):
        if not visited[nxt]:
            visited[nxt] = True
            queue.append(nxt)

Problem 2: Baekjoon 2178 – Maze Exploration (Silver I)

Problem Description

Shortest distance in an NxM maze from (1,1) → (N,M) (number of moves). '1' = passable, '0' = wall.

Input format

4 6
101111
101010
111010
000110

Approach

  • BFS for shortest distance from (0,0) → (N-1,M-1)
  • dist[0][0] = 1 (including starting point)

Notes

  • Start counting distance from 1 (including starting point)
  • String input → check if each character is '1'

Problem 3: Baekjoon 7576 – Tomato (Gold V)

Problem Description

In an MxN box, ripe tomatoes (1) ripen adjacent tomatoes in one day. Print the minimum number of days for all tomatoes to ripen. If impossible, print -1.

Approach

Multi-start BFS:

queue = deque()
for r in range(n):
    for c in range(m):
        if grid[r][c] == 1:
            queue.append((r, c))
            dist[r][c] = 0

Notes

  • 0 = unripe tomato, 1 = ripe tomato, -1 = empty cell
  • If all tomatoes are ripe from the start, print 0
  • Do not explore empty cells (-1)

Pre-submission Checklist

  • 1260: BFS after sorting adjacent nodes?
  • 2178: Initial value of dist 1 (including starting point)?
  • 2178: Is range check correct?
  • 7576: Did you put all multiple starting points into the queue?
  • 7576: Handle so that empty cells (-1) cannot be passed?
  • 7576: Print 0 if all are ripe from the start?

Model Answer

Refer to the reference solution below.

Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

boj_1260.pyPython
"""Baekjoon 1260 – DFS and BFS
https://www.acmicpc.net/problem/1260
Time  : O((V + E) log V)
Space : O(V + E)"""

import sys
from collections import deque

input = sys.stdin.readline
sys.setrecursionlimit(10 ** 6)


def main() -> None:
    n, m, v = map(int, input().split())
    graph: list[list[int]] = [[] for _ in range(n + 1)]

    for _ in range(m):
        a, b = map(int, input().split())
        graph[a].append(b)
        graph[b].append(a)

    for i in range(1, n + 1):
        graph[i].sort()

    # DFS
    visited_dfs = [False] * (n + 1)
    dfs_result: list[int] = []

    def dfs(node: int) -> None:
        visited_dfs[node] = True
        dfs_result.append(node)
        for nxt in graph[node]:
            if not visited_dfs[nxt]:
                dfs(nxt)

    dfs(v)
    print(' '.join(map(str, dfs_result)))

    # BFS
    visited_bfs = [False] * (n + 1)
    queue: deque[int] = deque([v])
    visited_bfs[v] = True
    bfs_result: list[int] = []

    while queue:
        node = queue.popleft()
        bfs_result.append(node)
        for nxt in graph[node]:
            if not visited_bfs[nxt]:
                visited_bfs[nxt] = True
                queue.append(nxt)

    print(' '.join(map(str, bfs_result)))


if __name__ == "__main__":
    main()
boj_2178.pyPython
"""Baekjoon 2178 – Maze Exploration
https://www.acmicpc.net/problem/2178
Time  : O(N × M)
Space : O(N × M)

Strategy: BFS shortest path (count the number of moves including the starting point)"""

import sys
from collections import deque

input = sys.stdin.readline

DR = [-1, 1, 0, 0]
DC = [0, 0, -1, 1]


def main() -> None:
    n, m = map(int, input().split())
    maze = [list(map(int, list(input().rstrip()))) for _ in range(n)]

    dist = [[-1] * m for _ in range(n)]
    dist[0][0] = 1    #Including the starting point
    queue: deque[tuple[int, int]] = deque([(0, 0)])

    while queue:
        r, c = queue.popleft()
        if r == n - 1 and c == m - 1:
            break
        for i in range(4):
            nr, nc = r + DR[i], c + DC[i]
            if 0 <= nr < n and 0 <= nc < m and maze[nr][nc] == 1 and dist[nr][nc] == -1:
                dist[nr][nc] = dist[r][c] + 1
                queue.append((nr, nc))

    print(dist[n - 1][m - 1])


if __name__ == "__main__":
    main()
boj_7576.pyPython
"""Baekjoon 7576 – Tomato
https://www.acmicpc.net/problem/7576
Time  : O(M × N)
Space : O(M × N)

Strategy: Multi-source BFS — start simultaneously from all ripe tomatoes"""

import sys
from collections import deque

input = sys.stdin.readline

DR = [-1, 1, 0, 0]
DC = [0, 0, -1, 1]


def main() -> None:
    m, n = map(int, input().split())   #m=number of columns, n=number of rows
    grid = [list(map(int, input().split())) for _ in range(n)]

    dist = [[-1] * m for _ in range(n)]
    queue: deque[tuple[int, int]] = deque()

    #Insert all ripe tomatoes into the queue
    for r in range(n):
        for c in range(m):
            if grid[r][c] == 1:
                dist[r][c] = 0
                queue.append((r, c))

    while queue:
        r, c = queue.popleft()
        for i in range(4):
            nr, nc = r + DR[i], c + DC[i]
            if 0 <= nr < n and 0 <= nc < m and grid[nr][nc] == 0 and dist[nr][nc] == -1:
                dist[nr][nc] = dist[r][c] + 1
                queue.append((nr, nc))

    #Calculate results
    ans = 0
    for r in range(n):
        for c in range(m):
            if grid[r][c] == 0 and dist[r][c] == -1:
                print(-1)
                return
            if dist[r][c] != -1:
                ans = max(ans, dist[r][c])

    print(ans)


if __name__ == "__main__":
    main()

Learn this lesson with a mentor

Work through the Python Algorithms & Data Structures track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now