|
| 1 | +''' |
| 2 | +# 207 |
| 3 | +μ°Έκ³ μμ: https://www.youtube.com/watch?v=EgI5nU9etnU |
| 4 | +λ¬Έμ νμ΄: https://www.algodale.com/problems/course-schedule/ |
| 5 | +
|
| 6 | +## λ¬Έμ μ 리 |
| 7 | +π prerequisitesλ? νμ μ μ κ³Όλͺ©μ΄λ€. |
| 8 | +π λ°©ν₯μ±μ΄ μλ μ°κ²° κ΄κ³μ΄λ―λ‘, Directed Graphλ€. |
| 9 | +π Cycle λ°μ μ, μ½μ€λ₯Ό μ΄μν μ μλ€.(μλ‘ μμ‘΄νλ μνμ΄ μμ΄μ λμμ΄ λκ² λλ κ²½μ°) |
| 10 | +
|
| 11 | +## ν΄κ²° λ°©μ λκ°μ§ |
| 12 | +1. BFS, Queue, Topological Sort: μμ μ λ ¬ |
| 13 | +2. DFS, Cycle Detection: μν νμ§ |
| 14 | +
|
| 15 | +### μμ μ λ ¬(Topological Sort) - BFS, Queue |
| 16 | +- μ§μ
μ°¨μ(indegree): λ
Έλλ‘ λ€μ΄μ€λ νμ΄ν μ |
| 17 | +- κ·Έλνλ‘ μΈμ 리μ€νΈ κ΅¬μ± |
| 18 | +- Queueμ λ£κΈ° |
| 19 | +- Queue BFS νμ |
| 20 | +- λͺ¨λ κ³Όλͺ©μ λ€μλμ§ νμΈ |
| 21 | +
|
| 22 | +### μν νμ§(Cycle Detection) - DFS |
| 23 | +- κ·Έλνλ‘ μΈμ 리μ€νΈ κ΅¬μ± |
| 24 | +- λ°©λ¬Έ μν λ°°μ΄ μ΄κΈ°ν |
| 25 | +- dfs ν¨μ |
| 26 | +- λͺ¨λ λ
Έλμ λν΄ dfs μ€ν |
| 27 | +
|
| 28 | +## TC & SC |
| 29 | +- μκ° λ³΅μ‘λμ κ³΅κ° λ³΅μ‘λλ O(V + E)λ‘ λμΌνλ€. |
| 30 | +``` |
| 31 | +V: λ
Έλ μ(κ³Όλͺ© μ) |
| 32 | +E: κ°μ μ(μ μ κ³Όλͺ© κ΄κ³ μ) |
| 33 | +``` |
| 34 | +
|
| 35 | +### TC is O(V + E) |
| 36 | +
|
| 37 | +λ λ°©λ² λͺ¨λ, κ·Έλνμ λͺ¨λ λ
Έλμ κ°μ μ ν λ²μ© νμΈν¨ |
| 38 | +- BFS: λͺ¨λ Vλ₯Ό μννλ©΄μ, κ° λ
Έλμμ λκ°λ Eλ₯Ό λ°λΌκ°λ©° μ°¨μλ₯Ό μ€μ |
| 39 | +- DFS: λͺ¨λ Vλ₯Ό μννλ©΄μ, κ° λ
Έλμμ μ°κ²°λ Eλ₯Ό λ°λΌκ°λ©° κΉμ΄ νμ |
| 40 | +
|
| 41 | +### SC is O(V + E) |
| 42 | +- O(V+E): V + Eλ₯Ό μ μ₯νλ μΈμ 리μ€νΈ κ·Έλν |
| 43 | +- O(V)'s: λ°©λ¬Έ μν μ μ₯, μ§μ
μ°¨μ λ°°μ΄, BFS ν, DFS νΈμΆ μ€ν |
| 44 | +
|
| 45 | +## μμμ λ ¬(BFS) vs μννμ§(DFS)π€ |
| 46 | +
|
| 47 | +### BFSλ₯Ό μ¬μ©νμ λ |
| 48 | +- λ°λ³΅λ¬Έμ μ¬μ©ν BFSκ° indegree(μ§μ
μ°¨μ) κ°λ
μ΄ λ³΄λ€ μ§κ΄μ μ΄λ―λ‘ => "μμλλ‘ μ²λ¦¬ν μ μλμ§ νμΈ"ν λ λͺ
ννκ² μ¬μ©ν μ μλ€. μ§μ
μ°¨μκ° 0μΈ λ
ΈλλΆν° μμν΄μ μ²λ¦¬ |
| 49 | +- μ μ κ³Όλͺ©μ λ€ λ€μ κ³Όλͺ©μ μ§μ
μ°¨μκ° 0μ΄ λλ―λ‘ λ€μ΄κ° μ μλ κ³Όλͺ©μ΄λΌλ μ μ΄ λͺ
νν¨ |
| 50 | +``` |
| 51 | +ν€μλ: μ²λ¦¬ μμλ₯Ό μΆλ ₯, μμλλ‘ μ²λ¦¬ν μ μλμ§ |
| 52 | +``` |
| 53 | +
|
| 54 | +### DFSλ₯Ό μ¬μ©νμ λ |
| 55 | +- DFS μν νμ§λ "μν μ¬λΆ"κ° ν΅μ¬μΌ λ μμ°μ€λ½λ€. |
| 56 | +- μν(Status)λ₯Ό μ¬μ©ν΄μ, λ°©λ¬Έ μ€μΈ λ
Έλ μνλ₯Ό λ€μ λ°©λ¬Ένλ€λ©΄ μνμ΄ μμμ λ°λ‘ μ μ μλ€. |
| 57 | +- μνμ΄ λ°κ²¬λλ©΄ λ°λ‘ μ€λ¨νλ―λ‘, μν νμ§μ μμ°μ€λ½λ€. |
| 58 | +``` |
| 59 | +ν€μλ: μ¬μ΄ν΄μ΄ μλμ§ νλ¨ |
| 60 | +``` |
| 61 | +
|
| 62 | +### +a) `@cache`λ₯Ό νμ©ν΄λ³΄μ. |
| 63 | +- νμ΄μ 3.9~ λ©λͺ¨μ΄μ μ΄μ
ν¨μ |
| 64 | +- μμ ν¨μ + μ¬κ· μ΅μ νμ μ¬μ© (μΈλΆ μμ‘΄μ±, λΆμν¨κ³Όμ μ£Όμν κ²) |
| 65 | +''' |
| 66 | +from enum import Enum |
| 67 | + |
| 68 | +class Status(Enum): # use it to dfs |
| 69 | + INITIAL = 1 |
| 70 | + IN_PROGRESS = 2 |
| 71 | + FINISHED = 3 |
| 72 | + |
| 73 | +class Solution: |
| 74 | + ''' |
| 75 | + 1. BFS |
| 76 | + μμ μ λ ¬ |
| 77 | + ''' |
| 78 | + def canFinishTopologicalSort(self, numCourses: int, prerequisites: List[List[int]]) -> bool: |
| 79 | + indegree = [0] * numCourses |
| 80 | + graph = defaultdict(list) |
| 81 | + |
| 82 | + for dest, src in prerequisites: |
| 83 | + graph[src].append(dest) |
| 84 | + indegree[dest] += 1 |
| 85 | + |
| 86 | + queue = deque([i for i in range(numCourses) if indegree[i] == 0]) |
| 87 | + processed_count = 0 |
| 88 | + |
| 89 | + while queue: |
| 90 | + node = queue.popleft() |
| 91 | + processed_count += 1 |
| 92 | + for neighbor in graph[node]: |
| 93 | + indegree[neighbor] -= 1 |
| 94 | + if indegree[neighbor] == 0: |
| 95 | + queue.append(neighbor) |
| 96 | + |
| 97 | + return processed_count == numCourses |
| 98 | + |
| 99 | + ''' |
| 100 | + 2. DFS |
| 101 | + μν νμ§ |
| 102 | + ''' |
| 103 | + def canFinishCycleDetection(self, numCourses: int, prerequisites: List[List[int]]) -> bool: |
| 104 | + graph = defaultdict(list) |
| 105 | + |
| 106 | + for dest, src in prerequisites: |
| 107 | + graph[src].append(dest) |
| 108 | + |
| 109 | + statuses = {i: Status.INITIAL for i in range(numCourses)} |
| 110 | + |
| 111 | + def dfs(node): |
| 112 | + if statuses[node] == Status.IN_PROGRESS: |
| 113 | + return False |
| 114 | + if statuses[node] == Status.FINISHED: |
| 115 | + return True |
| 116 | + |
| 117 | + statuses[node] = Status.IN_PROGRESS |
| 118 | + for neighbor in graph[node]: |
| 119 | + if not dfs(neighbor): |
| 120 | + return False |
| 121 | + statuses[node] = Status.FINISHED |
| 122 | + return True |
| 123 | + |
| 124 | + return all(dfs(crs) for crs in range(numCourses)) |
| 125 | + |
| 126 | + ''' |
| 127 | + 3. @cache |
| 128 | +
|
| 129 | + νμ΄μ¬ 3.9 μ΄μμμ μ¬μ©νλ λ©λͺ¨μ΄μ μ΄μ
λ°μ½λ μ΄ν° |
| 130 | + - λμΌ μ
λ ₯ -> λμΌ μΆλ ₯μ 보μ₯νλ€. |
| 131 | + - 128κ° κΉμ§λ§ μ μ₯νλ @lru_cacheλ μλ€. |
| 132 | + ''' |
| 133 | + def canFinishWithCache(self, numCourses: int, prerequisites: List[List[int]]) -> bool: |
| 134 | + graph = defaultdict(list) |
| 135 | + |
| 136 | + for dest, src in prerequisites: |
| 137 | + graph[src].append(dest) |
| 138 | + |
| 139 | + traversing = set() |
| 140 | + |
| 141 | + @cache |
| 142 | + def dfs(node): |
| 143 | + if node in traversing: |
| 144 | + return False |
| 145 | + |
| 146 | + traversing.add(node) |
| 147 | + result = all(dfs(pre) for pre in graph[node]) |
| 148 | + traversing.remove(node) |
| 149 | + return result |
| 150 | + |
| 151 | + return all(dfs(node) for node in range(numCourses)) |
| 152 | + |
| 153 | + ''' |
| 154 | + 4. visitedκ³Ό ν¨κ» μ¬μ©νκΈ° |
| 155 | +
|
| 156 | + @cache λ°μ½λ μ΄ν°λ λ©λͺ¨μ΄μ μ΄μ
, κ°μ μ
λ ₯κ°μ λ°λΌ κ°μ κ²°κ³Όλ₯Ό λ°ννκ²ν¨ |
| 157 | + κ²°κ³Όκ° λ³νμ§ μμ λ μ μ©ν¨ => dfs(node)λ μΈλΆ μν μν traversingμ μμ‘΄ν΄μ λμμ΄ λ¬λΌμ§ μ μλ€. |
| 158 | + λ°λΌμ visited setμ΄ λ μμ°μ€λ¬μΈ μ μλ€ |
| 159 | + ''' |
| 160 | + def canFinishWithVisited(self, numCourses: int, prerequisites: List[List[int]]) -> bool: |
| 161 | + graph = defaultdict(list) |
| 162 | + |
| 163 | + for dest, src in prerequisites: |
| 164 | + graph[src].append(dest) |
| 165 | + |
| 166 | + traversing = set() |
| 167 | + visited = set() |
| 168 | + |
| 169 | + def dfs(node): |
| 170 | + if node in traversing: |
| 171 | + return False |
| 172 | + if node in visited: |
| 173 | + return True |
| 174 | + |
| 175 | + traversing.add(node) |
| 176 | + for pre in graph[node]: |
| 177 | + if not dfs(pre): |
| 178 | + return False |
| 179 | + traversing.remove(node) |
| 180 | + |
| 181 | + visited.add(node) |
| 182 | + return True |
| 183 | + |
| 184 | + return all(dfs(i) for i in range(numCourses)) |
0 commit comments