====== tgraph.py ======
===== imports and globals =====
import collections
import heapq
from typing import Mapping, Sequence, Optional, Iterable, List
Graph = Sequence[Iterable[int]]
WGraph = Sequence[Mapping[int, float]]
INF = float('inf')
===== dfs =====
==== 코드 ====
# N dfs
# I {"version": "1.0", "typing": ["List", "Generator", "Iterable", "Optional", "Sequence"], "const": ["Graph"]}
def dfs(graph: Graph,
source: int,
stack: Optional[List] = None,
yields_on_leave: bool = False) -> Generator[int, None, None]:
is_visited = [False] * len(graph)
if stack is None:
stack = []
is_visited[source] = True
stack.append(source)
iter_stack = [iter(graph[source])]
yield source
while iter_stack:
try:
node = next(iter_stack[-1])
if not is_visited[node]:
is_visited[node] = True
stack.append(node)
iter_stack.append(iter(graph[node]))
yield node
except StopIteration:
if yields_on_leave:
yield stack[-1]
stack.pop()
iter_stack.pop()
==== 설명 ====
* non-recursive DFS
==== 이 코드를 사용하는 문제 ====
---- struct table ----
schema: ps
cols: site, prob_id, %title%, prob_level
filter: teflib ~ *dfs*
csv: 0
----
===== min_distances =====
==== 코드 ====
# N min_distances
# I {"version": "1.0", "import": ["collections"], "typing": ["List", "Iterable", "Optional", "Sequence"], "const": ["Graph", "INF"]}
def min_distances(graph: Graph,
source: int,
sink: Optional[int] = None) -> List[int]:
"""Returns shortest path distances from source node to all nodes."""
distances = [INF] * len(graph)
distances[source] = 0
queue = collections.deque([(source, 0)])
while queue:
u, dist = queue.popleft()
for v in graph[u]:
if distances[v] == INF:
distances[v] = dist + 1
if v == sink:
return distances
queue.append((v, dist + 1))
return distances
==== 설명 ====
* [[ps:단일 출발지 최단 경로]] 참고
==== 이 코드를 사용하는 문제 ====
{{topic>ps:teflib:min_distances&nouser&nodate}}
===== min_distances_on_tree =====
==== 코드 ====
# N min_distances_on_tree
# I {"version": "1.0", "typing": ["List", "Mapping", "Optional", "Sequence"], "const": ["WGraph", "INF"]}
def min_distances_on_tree(wgraph: WGraph,
source: int,
sink: Optional[int] = None) -> List[float]:
"""Returns shortest path distances from source node to all nodes."""
distances = [INF] * len(wgraph)
distances[source] = 0
stack = [(source, 0, None)]
while stack:
u, dist_to_u, parent = stack.pop()
distances[u] = dist_to_u
if u == sink:
return distances
for v, dist in wgraph[u].items():
if v != parent:
stack.append((v, dist_to_u + dist, u))
return distances
==== 설명 ====
* [[ps:단일 출발지 최단 경로]] 참고
==== 이 코드를 사용하는 문제 ====
{{topic>ps:teflib:min_distances_on_tree&nouser&nodate}}
===== minimum_spanning_tree =====
==== 코드 ====
# N minimum_spanning_tree
# I {"version": "1.0", "import": ["heapq"], "typing": ["Mapping", "Sequence"], "const": ["WGraph"]}
def minimum_spanning_tree(wgraph: WGraph) -> int:
"""Returns sum of weights in the minimum spanning tree of given graph.
Based on Prim's algorithm with binary heap, running in O(ElogV)."""
total_cost = 0
is_visited = [False] * len(wgraph)
heap = [(0, 0)]
while heap:
cost, u = heapq.heappop(heap)
if is_visited[u]:
continue
is_visited[u] = True
total_cost += cost
for v, cost_to_v in wgraph[u].items():
heapq.heappush(heap, (cost_to_v, v))
return total_cost
==== 설명 ====
* [[ps:최소 신장 트리]] 참고
==== 이 코드를 사용하는 문제 ====
{{topic>tag:teflib:minimum_spanning_tree&nouser&nodate}}
===== all_pairs_shortest_path =====
==== 코드 ====
# N all_pairs_shortest_path
# I {"version": "1.0", "typing": ["List", "Mapping", "Sequence"], "const": ["WGraph", "INF"]}
def all_pairs_shortest_path(wgraph: WGraph) -> List[List[int]]:
"""Returns a table that contains shortest distances between all nodes."""
n = len(wgraph)
dists = [[INF] * n for _ in range(n)]
for i in range(n):
dists[i][i] = 0
for dists_u, edges in zip(dists, wgraph):
for v, dist_to_v in edges.items():
dists_u[v] = dist_to_v
for k, dists_k in enumerate(dists):
for dists_i in dists:
dists_ik = dists_i[k]
for j, (dists_ij, dists_kj) in enumerate(zip(dists_i, dists_k)):
if dists_ik + dists_kj < dists_ij:
dists_i[j] = dists_ik + dists_kj
return dists
==== 설명 ====
* [[ps:전체쌍 최단 경로]] 참고
* 인풋으로 인접행렬 형태의 그래프를 받으면, 지금처럼 인접리스트를 인접행렬로 변환하는 과정이 필요없으므로 더 빠르겠지만, 다른 함수들과의 일관성을 위해서 인접리스트를 받는 것으로 했다.
* 대신 속도를 만회하기 위해서 코드를 좀더 최적화 했다. O(n^3)이다보니 사소한 최적화에도 실행속도가 꽤 영향을 받는다.
==== 이 코드를 사용하는 문제 ====
{{topic>tag:teflib:all_pairs_shortest_path&nouser&nodate}}
===== dijkstra =====
==== 코드 ====
# N dijkstra
# I {"version": "1.0", "import": ["heapq"], "typing": ["List", "Mapping", "Optional", "Sequence"], "const": ["WGraph", "INF"]}
def dijkstra(wgraph: WGraph,
source: int,
sink: Optional[int] = None) -> List[float]:
"""Returns a list of minimum costs from source to each node.
This is an implementation of Dijkstra algorithm using binary heap, running
in O(ElogV).
"""
distances = [INF] * len(wgraph)
distances[source] = 0
heap = [(0, source)]
while heap:
dist_u, u = heapq.heappop(heap)
if dist_u != distances[u]:
continue
if u == sink:
return distances
for v, dist_uv in wgraph[u].items():
dist_v = dist_u + dist_uv
if dist_v < distances[v]:
distances[v] = dist_v
heapq.heappush(heap, (dist_v, v))
return distances
==== 설명 ====
* [[ps:단일 출발지 최단 경로#다익스트라 알고리즘]] 참고
==== 이 코드를 사용하는 문제 ====
{{topic>tag:teflib:dijkstra&nouser&nodate}}
===== spfa =====
==== 코드 ====
# N spfa
# I {"version": "1.4", "import": ["collections"], "typing": ["List", "Mapping", "Optional", "Sequence"], "const": ["WGraph", "INF"]}
def spfa(wgraph: WGraph,
source: int,
prev: Optional[List[int]] = None) -> List[float]:
size = len(wgraph)
lengths = [0] * size
is_in_queue = [False] * size
is_in_queue[source] = True
distances = [INF] * size
distances[source] = 0
if prev:
prev[source] = None
queue = collections.deque([source])
while queue:
u = queue.popleft()
is_in_queue[u] = False
length_u, dist_u = lengths[u], distances[u]
if length_u == size:
dist_u = distances[u] = -INF
for v, dist_uv in wgraph[u].items():
dist_v = dist_u + dist_uv
if distances[v] <= dist_v:
continue
distances[v], lengths[v] = dist_v, length_u + 1
if prev:
prev[v] = u
if not is_in_queue[v]:
queue.append(v)
is_in_queue[v] = True
return distances
==== 설명 ====
* [[ps:단일 출발지 최단 경로#Shortest Path Faster Algorithm (SPFA)]] 참고
==== 이 코드를 사용하는 문제 ====
---- struct table ----
schema: ps
cols: site, prob_id, %title%, prob_level
filter: teflib ~ *spfa*
csv: 0
----