Python Algorithms & Data Structures track

Lesson 16 · Part 4: Graph Foundations

Topological Sort

Kahn (BFS) and DFS topological sort, cycle detection

Part 4: Graph Foundations3 min readFree lesson

Part 04 – Basics of Graphs | Difficulty: ⭐⭐⭐⭐☆


Introduction: DAG and Precedence Relationships

Topological Sort determines the order of nodes in a Directed Acyclic Graph (DAG) to satisfy all precedence relationships.

Example Problem Types:

Example Graph Purpose
Course Order Prerequisite → Subsequent Course Determine the order in which courses can be taken
Build System Dependency Relationships Determine compilation order
Line Up A comes before B Possible lineup order
Task Scheduling Task Dependencies Determine execution order

Learning Goals

  1. Implement topological sort using Kahn's Algorithm (BFS-based).
  2. Understand that the reverse of the post-order in DFS-based topological sort is the topological order.
  3. Perform cycle detection and unique order determination using topological sort.

Key Concepts

In-degree

Graph:
  1 → 3
  2 → 3
  3 → 4 → 5
  2 → 5

Entry order:
1: 0 (Nothing points to 1)
  2: 0
3: 2 (1 and 2 point)
  4: 1
  5: 2

Kahn's Algorithm (BFS-based)

1. Insert all nodes with in-degree 0 into the queue
2. Remove a node from the queue and add it to the result
3. Decrease the in-degree of all adjacent nodes of that node by 1
4. Insert nodes whose in-degree becomes 0 into the queue
5. Repeat until the queue is empty
6. If the result length < V, a cycle exists

Simulation:
  in-degree: {1:0, 2:0, 3:2, 4:1, 5:2}
Queue: [1, 2]
→ pop(1): result=[1], in-degree of 3=1
Queue: [2]
→ pop(2): result=[1,2], in-degree of 3=0, in-degree of 5=1
Queue: [3]
→ pop(3): result=[1,2,3], in-degree of 4=0
Queue: [4]
→ pop(4): result=[1,2,3,4], in-degree of 5=0
Queue: [5]
→ pop(5): result=[1,2,3,4,5]  ← Complete

Topological Sort Diagram

DAG (Directed Acyclic Graph):
  1 ──→ 3 ──→ 5
  2 ──→ 3
  2 ──→ 4 ──→ 5

Possible phase sequences:
  [1, 2, 3, 4, 5]
  [1, 2, 4, 3, 5]
  [2, 1, 3, 4, 5]
  [2, 4, 1, 3, 5]
... (multiple possible)

Unique phase sequence: When there is always only 1 in the queue

Time/Space Complexity

  • Time: O(V + E)
  • Space: O(V) — in-degree array + queue

Example

File Contents
src/01_kahns_algorithm.py Kahn's BFS Topological Sort
src/02_dfs_topological.py DFS-based Topological Sort
src/03_cycle_detection.py Cycle Detection Using Topological Sort
src/04_unique_topological.py Determining Unique Topological Order

5 Common Mistakes

  1. Applying topological sorting to an undirected graph
  • Topological sorting can only be applied to directed graphs
  • If it is undirected, all edges are considered "bidirectional," so they are regarded as cycles
  1. Trusting the result without cycle checking

    result = kahns_sort(graph)
    #If there is a cycle, the result length < V
    if len(result) < n:
        print("Cycle exists!")
    
  2. Set the in-degree array size to n instead of n+1

    • 1-indexed: in_degree = [0] * (n + 1)
  3. Omitting in-degree updates when adding edges

    graph[u].append(v)
    in_degree[v] += 1    #← Must go together
    
  4. Implementing lexicographically smallest topological sort without a priority queue

  • If a lexicographically smallest topological sort is needed, use heapq (min heap)
  • A normal queue (deque) only guarantees insertion order

Summary

Purpose Method
Basic topological sort Kahn's (BFS)
Graph with cycles Kahn's → check the result length
DFS-based Reverse of post-order
Determining a unique order Check if there is always only 1 node in the queue
Lexicographically smallest use heapq

Try It Yourself

  1. Find the order in which the 5 subjects [A→C, B→C, C→D, B→D] can be taken.
  2. What happens if you apply topological sorting to [1→2, 2→3, 3→1]?
  3. Create an example of a graph that has a unique topological order.

Example files

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

src/01_kahns_algorithm.pyPython
"""Topic  : Kahn's Algorithm — BFS-based Topological Sort
Time   : O(V + E)
Space  : O(V)"""

from collections import deque
import heapq


#── Kahn's Topological Sort ──────────────────────────────────────────────────────────

def kahns_sort(
    n: int, graph: list[list[int]]
) -> list[int] | None:
    """Topological sort using Kahn's algorithm.

    Returns:
        List of topological order, None if there is a cycle"""
    in_degree = [0] * (n + 1)
    for u in range(1, n + 1):
        for v in graph[u]:
            in_degree[v] += 1

    queue: deque[int] = deque(
        [i for i in range(1, n + 1) if in_degree[i] == 0]
    )
    result: list[int] = []

    while queue:
        node = queue.popleft()
        result.append(node)
        for nxt in graph[node]:
            in_degree[nxt] -= 1
            if in_degree[nxt] == 0:
                queue.append(nxt)

    if len(result) < n:
        return None    #Cycle Exists

    return result


def kahns_sort_verbose(
    n: int, graph: list[list[int]]
) -> list[int] | None:
    """Step-by-step output of Kahn's algorithm."""
    in_degree = [0] * (n + 1)
    for u in range(1, n + 1):
        for v in graph[u]:
            in_degree[v] += 1

    print(f"Initial In-Degree: {in_degree[1:]}")

    queue: deque[int] = deque(
        [i for i in range(1, n + 1) if in_degree[i] == 0]
    )
    result: list[int] = []
    step = 0

    while queue:
        node = queue.popleft()
        result.append(node)
        step += 1
        updated = []
        for nxt in graph[node]:
            in_degree[nxt] -= 1
            updated.append(nxt)
            if in_degree[nxt] == 0:
                queue.append(nxt)
        print(f"Step{step:2d}: pop={node},"
              f"In-Degree Decrease: {updated},"
              f"Queue: {list(queue)}")

    return result if len(result) == n else None


#── Lexicographically Minimum Topological Sort (using heapq) ───────────────────────────

def kahns_sort_lex(
    n: int, graph: list[list[int]]
) -> list[int] | None:
    """Lexicographically smallest topological sort (using heap)."""
    in_degree = [0] * (n + 1)
    for u in range(1, n + 1):
        for v in graph[u]:
            in_degree[v] += 1

    heap: list[int] = [i for i in range(1, n + 1) if in_degree[i] == 0]
    heapq.heapify(heap)
    result: list[int] = []

    while heap:
        node = heapq.heappop(heap)
        result.append(node)
        for nxt in graph[node]:
            in_degree[nxt] -= 1
            if in_degree[nxt] == 0:
                heapq.heappush(heap, nxt)

    return result if len(result) == n else None


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

if __name__ == "__main__":
    #Construct DAG
    #  1 → 3 → 5
    #  2 → 3
    #  2 → 4 → 5
    n = 5
    edges = [(1, 3), (2, 3), (2, 4), (3, 5), (4, 5)]
    graph: list[list[int]] = [[] for _ in range(n + 1)]
    for u, v in edges:
        graph[u].append(v)

    print("=" * 55)
    print("Kahn's Topological Sort")
    print("=" * 55)
    result = kahns_sort_verbose(n, graph)
    print(f"\nTopological Sort Result: {result}")

    #Lexicographical Comparison
    print("\n=== Normal vs Lexicographical Minimum ===")
    result_lex = kahns_sort_lex(n, graph)
    print(f"Normal  : {result}")
    print(f"Lexicographical: {result_lex}")

    #Cycle Case
    print("\n=== Cycle Detection ===")
    n2 = 3
    g2: list[list[int]] = [[] for _ in range(n2 + 1)]
    for u, v in [(1, 2), (2, 3), (3, 1)]:
        g2[u].append(v)
    result2 = kahns_sort(n2, g2)
    print(f"Cycle Graph Result: {result2}  (None means cycle)")

    #Course Enrollment Example
    print("\n=== Course Enrollment Order ===")
    courses = {
        "Mathematics": 1, "Physics": 2, "Chemistry": 3,
        "Engineering Mathematics": 4, "Physics": 5, "Chemical Engineering": 6
    }
    prereqs = [
        (courses["Mathematics"], courses["Engineering Mathematics"]),
        (courses["Mathematics"], courses["Physics"]),
        (courses["Physics"], courses["Physics"]),
        (courses["Chemistry"], courses["Chemical Engineering"]),
        (courses["Engineering Mathematics"], courses["Chemical Engineering"]),
    ]
    n3 = len(courses)
    g3: list[list[int]] = [[] for _ in range(n3 + 1)]
    for u, v in prereqs:
        g3[u].append(v)
    order = kahns_sort(n3, g3)
    rev_courses = {v: k for k, v in courses.items()}
    if order:
        print(f"Enrollment Order: {[rev_courses[i] for i in order]}")
src/02_dfs_topological.pyPython
"""Topic  : DFS-based topological sort — reverse of post-order
Time   : O(V + E)
Space  : O(V) recursive stack"""

import sys
sys.setrecursionlimit(100_000)


#── DFS Topological Sort ─────────────────────────────────────────────────────────────

def dfs_topological_sort(
    n: int, graph: list[list[int]]
) -> list[int] | None:
    """DFS-based topological sort.

    The reverse of post-order is the topological order.

    Returns:
        Topological order, None if there is a cycle"""
    WHITE = 0   #Unvisited
    GRAY = 1    #Visiting (in DFS stack)
    BLACK = 2   #Fully Explored

    color = [WHITE] * (n + 1)
    post_order: list[int] = []
    has_cycle = [False]

    def dfs(node: int) -> None:
        color[node] = GRAY
        for nxt in graph[node]:
            if color[nxt] == GRAY:
                has_cycle[0] = True
                return
            if color[nxt] == WHITE:
                dfs(nxt)
                if has_cycle[0]:
                    return
        color[node] = BLACK
        post_order.append(node)

    for node in range(1, n + 1):
        if color[node] == WHITE:
            dfs(node)
            if has_cycle[0]:
                return None

    return post_order[::-1]


def dfs_topological_verbose(
    n: int, graph: list[list[int]]
) -> list[int] | None:
    """Step-by-step output of DFS topological sort."""
    WHITE, GRAY, BLACK = 0, 1, 2
    color = [WHITE] * (n + 1)
    post_order: list[int] = []
    has_cycle = [False]
    depth = [0]

    def dfs(node: int) -> None:
        color[node] = GRAY
        indent = "  " * depth[0]
        print(f"{indent}→ Enter node {node} (GRAY)")
        depth[0] += 1
        for nxt in graph[node]:
            if color[nxt] == GRAY:
                print(f"{indent}  ⚠ Cycle detected! {nxt} → {node}")
                has_cycle[0] = True
                return
            if color[nxt] == WHITE:
                dfs(nxt)
                if has_cycle[0]:
                    return
        depth[0] -= 1
        color[node] = BLACK
        post_order.append(node)
        print(f"{indent}← Node {node} completed (BLACK), post_order={post_order}")

    for node in range(1, n + 1):
        if color[node] == WHITE:
            dfs(node)
            if has_cycle[0]:
                return None

    result = post_order[::-1]
    print(f"\npost_order    : {post_order}")
    print(f"Topological sort order : {result}")
    return result


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

if __name__ == "__main__":
    # DAG
    #  1 → 2 → 4
    #  1 → 3 → 4
    #         ↓
    #         5
    n = 5
    edges = [(1, 2), (1, 3), (2, 4), (3, 4), (4, 5)]
    graph: list[list[int]] = [[] for _ in range(n + 1)]
    for u, v in edges:
        graph[u].append(v)

    print("=" * 55)
    print("DFS-based topological sort")
    print("=" * 55)
    result = dfs_topological_verbose(n, graph)

    #Compare with Kahn's
    from collections import deque

    def kahns(n: int, graph: list[list[int]]) -> list[int]:
        in_deg = [0] * (n + 1)
        for u in range(1, n + 1):
            for v in graph[u]:
                in_deg[v] += 1
        q: deque[int] = deque(i for i in range(1, n + 1) if in_deg[i] == 0)
        res: list[int] = []
        while q:
            node = q.popleft()
            res.append(node)
            for nxt in graph[node]:
                in_deg[nxt] -= 1
                if in_deg[nxt] == 0:
                    q.append(nxt)
        return res

    kahn_result = kahns(n, graph)
    print(f"Kahn's result  : {kahn_result}")
    print(f"DFS result      : {result}")

    #Validation (both results must be valid topological orders)
    def verify_topological(order: list[int], n: int, edges: list[tuple[int, int]]) -> bool:
        pos = {v: i for i, v in enumerate(order)}
        return all(pos[u] < pos[v] for u, v in edges)

    print(f"Kahn's validity: {verify_topological(kahn_result, n, edges)}")
    print(f"DFS validity   : {verify_topological(result or [], n, edges)")

    #Cycle Case
    print("\n=== Cycle Detection ===")
    n2 = 4
    g2: list[list[int]] = [[] for _ in range(n2 + 1)]
    for u, v in [(1, 2), (2, 3), (3, 4), (4, 2)]:  #Cycle 2→3→4→2
        g2[u].append(v)
    result2 = dfs_topological_sort(n2, g2)
    print(f"Cycle Graph Result: {result2}  (None means cycle)")
src/03_cycle_detection.pyPython
"""Topic  : Cycle Detection — Topological Sort Based (Directed Graph)
Time   : O(V + E)
Space  : O(V)"""

import sys
sys.setrecursionlimit(100_000)

from collections import deque


#── Kahn's Based Cycle Detection ──────────────────────────────────────────────────

def has_cycle_kahns(n: int, graph: list[list[int]]) -> bool:
    """Cycle detection in directed graphs using Kahn's algorithm.

    If the length of the topological sort result is less than V, a cycle exists."""
    in_degree = [0] * (n + 1)
    for u in range(1, n + 1):
        for v in graph[u]:
            in_degree[v] += 1

    queue: deque[int] = deque(
        i for i in range(1, n + 1) if in_degree[i] == 0
    )
    visited_count = 0

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

    return visited_count < n


#── DFS Based Cycle Detection ─────────────────────────────────────────────────────

def has_cycle_dfs(n: int, graph: list[list[int]]) -> tuple[bool, list[int]]:
    """DFS-based cycle detection and returning the cycle path.

    Returns:
        (Whether a cycle exists, List of nodes in the cycle)"""
    WHITE, GRAY, BLACK = 0, 1, 2
    color = [WHITE] * (n + 1)
    parent = [-1] * (n + 1)
    cycle: list[int] = []

    def dfs(node: int) -> bool:
        color[node] = GRAY
        for nxt in graph[node]:
            if color[nxt] == GRAY:
                #Cycle Found — Reconstructing Path
                cycle.append(nxt)
                cur = node
                while cur != nxt:
                    cycle.append(cur)
                    cur = parent[cur]
                cycle.append(nxt)
                cycle.reverse()
                return True
            if color[nxt] == WHITE:
                parent[nxt] = node
                if dfs(nxt):
                    return True
        color[node] = BLACK
        return False

    for node in range(1, n + 1):
        if color[node] == WHITE:
            if dfs(node):
                return True, cycle

    return False, []


#── Examples of Graphs With/Without Cycles ─────────────────────────────────────────────

def demo_cycle_detection() -> None:
    """Cycle detection in various graphs."""
    test_cases = [
        #(n, edges, description)
        (4, [(1, 2), (2, 3), (3, 4)], "DAG (No Cycle)"),
        (4, [(1, 2), (2, 3), (3, 1)], "Triangle Cycle"),
        (5, [(1, 2), (2, 3), (3, 4), (4, 5), (5, 3)], "Long Cycle"),
        (4, [(1, 2), (1, 3), (2, 4), (3, 4)], "Diamond (No Cycle)"),
        (3, [(1, 1)], "Self Loop"),
    ]

    print("=== Cycle Detection Results ===")
    for n, edges, desc in test_cases:
        g: list[list[int]] = [[] for _ in range(n + 1)]
        for u, v in edges:
            g[u].append(v)

        has_c_k = has_cycle_kahns(n, g)
        has_c_d, cycle_path = has_cycle_dfs(n, g)

        print(f"\n{desc}:")
        print(f"Edges: {edges}")
        print(f"Kahn's: {'Cycle Exists' if has_c_k else 'No Cycle'}")
        print(f"DFS   : {'Cycle Exists' if has_c_d else 'No Cycle'}", end="")
        if cycle_path:
            print(f"Path: {' → '.join(map(str, cycle_path))}", end="")
        print()

        assert has_c_k == has_c_d, f"Algorithm Results Mismatch: {desc}"


#── Undirected Graph Cycle (DFS Based) ──────────────────────────────────────────

def has_cycle_undirected(
    n: int, graph: list[list[int]]
) -> bool:
    """Cycle detection in undirected graphs.

    Note: Different from directed graphs (excluding parent)"""
    visited = [False] * (n + 1)

    def dfs(node: int, parent: int) -> bool:
        visited[node] = True
        for nxt in graph[node]:
            if not visited[nxt]:
                if dfs(nxt, node):
                    return True
            elif nxt != parent:
                return True      #Node already visited but not the parent
        return False

    for node in range(1, n + 1):
        if not visited[node]:
            if dfs(node, -1):
                return True
    return False


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

if __name__ == "__main__":
    demo_cycle_detection()

    print("=== Undirected Graph Cycle Detection ===")
    #Triangle (Cycle Exists)
    n3 = 3
    g3: list[list[int]] = [[] for _ in range(n3 + 1)]
    for u, v in [(1, 2), (2, 3), (3, 1)]:
        g3[u].append(v)
        g3[v].append(u)
    print(f"Triangle: {has_cycle_undirected(n3, g3)}")

    #Tree (No Cycle)
    n4 = 4
    g4: list[list[int]] = [[] for _ in range(n4 + 1)]
    for u, v in [(1, 2), (1, 3), (2, 4)]:
        g4[u].append(v)
        g4[v].append(u)
    print(f"Tree  : {has_cycle_undirected(n4, g4)}")
src/04_unique_topological.pyPython
"""Topic  : Determining and Applying Unique Topological Order
Time   : O(V + E)
Space  : O(V)"""

from collections import deque
import heapq


#── Determining Unique Topological Order ─────────────────────────────────────────────────────

def is_unique_topological(
    n: int, graph: list[list[int]]
) -> tuple[bool, list[int]]:
    """Determine if a topological sort is unique.

    In Kahn's algorithm, if there is always only one element in the queue, it is unique.

    Returns:
        (whether it is unique, topological order)"""
    in_degree = [0] * (n + 1)
    for u in range(1, n + 1):
        for v in graph[u]:
            in_degree[v] += 1

    queue: deque[int] = deque(
        i for i in range(1, n + 1) if in_degree[i] == 0
    )
    result: list[int] = []
    is_unique = True

    while queue:
        if len(queue) > 1:
            is_unique = False   #Two or more → choice occurs
        node = queue.popleft()
        result.append(node)
        for nxt in graph[node]:
            in_degree[nxt] -= 1
            if in_degree[nxt] == 0:
                queue.append(nxt)

    if len(result) < n:
        return False, []   #Cycle

    return is_unique, result


#── Line Up Problem (Baekjoon 2252 Style) ────────────────────────────────────────

def lineup(
    n: int, conditions: list[tuple[int, int]]
) -> list[int] | None:
    """Order of lining up satisfying the conditions.

    conditions: (a, b) → a must be in front of b"""
    graph: list[list[int]] = [[] for _ in range(n + 1)]
    in_degree = [0] * (n + 1)

    for a, b in conditions:
        graph[a].append(b)
        in_degree[b] += 1

    queue: deque[int] = deque(
        i for i in range(1, n + 1) if in_degree[i] == 0
    )
    result: list[int] = []

    while queue:
        node = queue.popleft()
        result.append(node)
        for nxt in graph[node]:
            in_degree[nxt] -= 1
            if in_degree[nxt] == 0:
                queue.append(nxt)

    return result if len(result) == n else None


#── Priority Queue Topological Sort (Baekjoon 1766 Style) ─────────────────────────────────

def priority_topological(
    n: int,
    graph: list[list[int]],
    priority: list[int],
) -> list[int] | None:
    """Priority-based topological sort.

    Once all prerequisite courses are completed, take courses with smaller numbers first."""
    in_degree = [0] * (n + 1)
    for u in range(1, n + 1):
        for v in graph[u]:
            in_degree[v] += 1

    heap: list[int] = [
        priority[i] for i in range(1, n + 1) if in_degree[i] == 0
    ]
    heapq.heapify(heap)
    result: list[int] = []

    #Mapping from priority → original node
    pri_to_node = {priority[i]: i for i in range(1, n + 1)}

    while heap:
        pri = heapq.heappop(heap)
        node = pri_to_node[pri]
        result.append(node)
        for nxt in graph[node]:
            in_degree[nxt] -= 1
            if in_degree[nxt] == 0:
                heapq.heappush(heap, priority[nxt])

    return result if len(result) == n else None


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

if __name__ == "__main__":
    print("=== Determining Unique Topological Order ===")

    #Unique case
    n1 = 5
    g1: list[list[int]] = [[] for _ in range(n1 + 1)]
    for u, v in [(1, 2), (2, 3), (3, 4), (4, 5)]:  #Linear
        g1[u].append(v)
    unique1, order1 = is_unique_topological(n1, g1)
    print(f"Linear graph: unique={unique1}, order={order1}")

    #Non-unique case
    n2 = 4
    g2: list[list[int]] = [[] for _ in range(n2 + 1)]
    for u, v in [(1, 3), (2, 3), (3, 4)]:  #1,2 order free
        g2[u].append(v)
    unique2, order2 = is_unique_topological(n2, g2)
    print(f"Branched graph: unique={unique2}, order={order2}")

    #Line up
    print("\n=== Line Up (Baekjoon 2252 Style) ===")
    n3, conditions = 3, [(1, 3), (2, 3)]
    result = lineup(n3, conditions)
    print(f"n=3, conditions={conditions}")
    print(f"Order: {result}")

    #Priority topological sort
    print("\n=== Priority Topological Sort (Baekjoon 1766 Style) ===")
    #4 problems: has prerequisite relations + solve smaller numbers first
    n4 = 4
    g4: list[list[int]] = [[] for _ in range(n4 + 1)]
    for u, v in [(4, 2), (3, 1)]:   #4→2, 3→1 (prerequisite relations)
        g4[u].append(v)
    #Use itself as priority (number = priority)
    pri = [0] + list(range(1, n4 + 1))   # pri[i] = i
    result4 = priority_topological(n4, g4, pri)
    print(f"Result: {result4}")
    print("Solve 3, 4 (no prerequisites) first, and smaller numbers take priority")

    #Verify accuracy
    assert unique1 is True
    assert unique2 is False
    print("Accuracy check: PASS")

Assignment

Solve topological sorting problems using Kahn's algorithm and a priority queue.


Problem List

# Problem Tier Key Skill
1 Baekjoon 2252 – Line Up Gold III Basic Topological Sorting
2 Baekjoon 1766 – Problem Set Gold II heapq + Topological Sorting
3 Baekjoon 9205 – Walking with Beer Silver I BFS/DFS

Problem 1: Baekjoon 2252 – Line Up (Gold III)

Problem Description

Given N students and M conditions on the order to line up, print any possible line order.

Approach

Basic Kahn's topological sorting.

in_degree = [0] * (n + 1)
graph[a].append(b)
in_degree[b] += 1

Notes

  • Multiple answers possible → print any order
  • No cycle guaranteed (problem condition)

Problem 2: Baekjoon 1766 – Problem Set (Gold II)

Problem Description

Among N problems, there are M prerequisite conditions. Always solve the easier problem (smaller number) first.

Approach

heapq + topological sorting:

  • Use a min-heap instead of deque
  • Among the problems with all prerequisites completed, choose the one with the smallest number first
import heapq
heap = [i for i in range(1, n+1) if in_degree[i] == 0]
heapq.heapify(heap)

while heap:
    prob = heapq.heappop(heap)
    result.append(prob)
    for nxt in graph[prob]:
        in_degree[nxt] -= 1
        if in_degree[nxt] == 0:
            heapq.heappush(heap, nxt)

Problem 3: Baekjoon 9205 – Walking with Beer (Silver I)

Problem Description

Traveling from home to the festival, consume 1 bottle of beer every 50 meters, can carry up to 50 bottles at a time. Determine if it's possible to go via convenience stores.

Approach

If the Manhattan distance between two points is ≤ 1000, movement is possible. Determine whether it is possible to reach the festival from home.

def reachable(x1, y1, x2, y2):
    return abs(x1 - x2) + abs(y1 - y2) <= 1000

#Configure houses, convenience stores, and festivals as nodes
#Create movable edges and then perform BFS

Pre-submission Checklist

  • 2252: Should the size of the in-degree array be n+1 (1-indexed)?
  • 2252: Should all M conditions be added as one-way, not bidirectional?
  • 1766: Use heapq instead of deque?
  • 1766: Insert into the heap only after prerequisite conditions are completed?
  • 9205: Form edges based on the Manhattan distance ≤ 1000 condition?
  • 9205: Initialize for each test case?

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_1766.pyPython
"""Baekjoon 1766 – Problem Set
https://www.acmicpc.net/problem/1766
Time  : O((V + E) log V)
Space : O(V + E)

Strategy: heapq (min heap) + topological sort
         Complete prerequisite conditions + smaller numbers first"""

import sys
import heapq

input = sys.stdin.readline


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

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

    heap: list[int] = [i for i in range(1, n + 1) if in_degree[i] == 0]
    heapq.heapify(heap)
    result: list[int] = []

    while heap:
        prob = heapq.heappop(heap)
        result.append(prob)
        for nxt in graph[prob]:
            in_degree[nxt] -= 1
            if in_degree[nxt] == 0:
                heapq.heappush(heap, nxt)

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


if __name__ == "__main__":
    main()
boj_2252.pyPython
"""Baekjoon 2252 – Line Up
https://www.acmicpc.net/problem/2252
Time  : O(V + E)
Space : O(V + E)

Strategy: Kahn's Topological Sort"""

import sys
from collections import deque

input = sys.stdin.readline


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

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

    queue: deque[int] = deque(
        i for i in range(1, n + 1) if in_degree[i] == 0
    )
    result: list[int] = []

    while queue:
        node = queue.popleft()
        result.append(node)
        for nxt in graph[node]:
            in_degree[nxt] -= 1
            if in_degree[nxt] == 0:
                queue.append(nxt)

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


if __name__ == "__main__":
    main()
boj_9205.pyPython
"""Baekjoon 9205 – Walking While Drinking Beer
https://www.acmicpc.net/problem/9205
Time  : O(T × (N+2)²)   T = number of test cases
Space : O(N²)

Strategy: You can move if Manhattan distance ≤ 1000
          Determine whether it is possible to reach from home (0) to the festival (N+1) using BFS/DFS"""

import sys
from collections import deque

input = sys.stdin.readline


def main() -> None:
    t = int(input())
    for _ in range(t):
        n = int(input())
        home = list(map(int, input().split()))
        stores = [list(map(int, input().split())) for _ in range(n)]
        festival = list(map(int, input().split()))

        #House(0), Convenience store(1~n), Festival(n+1) nodes
        nodes = [home] + stores + [festival]
        total = n + 2

        def can_move(i: int, j: int) -> bool:
            return (abs(nodes[i][0] - nodes[j][0]) +
                    abs(nodes[i][1] - nodes[j][1])) <= 1000

        # BFS
        visited = [False] * total
        queue: deque[int] = deque([0])
        visited[0] = True
        found = False

        while queue:
            cur = queue.popleft()
            if cur == total - 1:
                found = True
                break
            for nxt in range(total):
                if not visited[nxt] and can_move(cur, nxt):
                    visited[nxt] = True
                    queue.append(nxt)

        print("happy" if found else "sad")


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