LeetCode/NeetCode

[위상정렬: 싸이클 탐지] 1462. Course Schedule IV

hyunkookim 2025. 4. 12. 05:54

1462. Course Schedule IV

 

class Solution:
    def checkIfPrerequisite(self, numCourses: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]:
        # numCourses: 전체 수업 개수 (0부터 numCourses-1까지 번호가 매겨짐)
        # prerequisites[i] = [a, b]: 수업 a를 먼저 들어야 b를 들을 수 있음 (a → b)
        # queries[j] = [u, v]: u가 v의 직접 또는 간접 선행 과목인지 확인해야 함

        # 1. 인접 리스트 (adjacency list)와 reachable 딕셔너리 초기화
        # adj[i]: 과목 i를 들은 후 들을 수 있는 과목 리스트
        # reachable[i]: 과목 i를 들은 후 도달 가능한 모든 과목을 저장하는 set
        adj = {}
        reachable = {}
        for i in range(numCourses):
            adj[i] = []         # i번 과목의 다음 과목들 리스트 초기화
            reachable[i] = set()  # i번 과목에서 도달 가능한 과목들을 저장할 집합 초기화

        # 2. prerequisites를 기반으로 인접 리스트 채우기
        for a, b in prerequisites:
            adj[a].append(b)  # a를 들으면 b를 들을 수 있으므로, adj[a]에 b 추가

        # 3. DFS 정의: start 과목에서 시작하여 방문 가능한 모든 과목을 reachable[start]에 저장
        def dfs(cur, start):
            for nxt in adj[cur]:  # 현재 과목(cur)에서 갈 수 있는 다음 과목들 순회
                if nxt not in reachable[start]:  # 아직 start에서 도달 가능한 목록에 없다면
                    reachable[start].add(nxt)    # 도달 가능한 과목으로 추가
                    dfs(nxt, start)              # 다음 과목에서 다시 탐색

        # 4. 모든 과목에 대해 DFS 수행하여 각 과목에서 도달 가능한 과목들 계산
        for i in range(numCourses):
            dfs(i, i)  # i번 과목을 시작점으로 dfs 수행하여 reachable[i] 채움

        # 5. 주어진 쿼리 처리
        res = []
        for u, v in queries:  # u가 v의 선행과목인지 확인
            if v not in reachable[u]:
                res.append(False)  # u에서 v까지 도달할 수 없으면 False
            else:
                res.append(True)   # u에서 v까지 도달 가능하면 True
        return res  # 모든 쿼리에 대한 결과 반환

 

🧠 핵심 요약:

  • reachable[u]은 과목 u를 들으면 직접 또는 간접적으로 들을 수 있는 모든 과목들.
  • 각 쿼리 [u, v]에 대해 v in reachable[u]만 확인하면 되므로 매우 효율적입니다.
  • 시간 복잡도는 최악의 경우 O(N^2)까지 갈 수 있으나, 수업 수가 적당히 제한된 경우 매우 빠릅니다.

https://youtu.be/cEW05ofxhn0