From 4fb7b1eba4a549f9adfbdaa322f828f71a7fa586 Mon Sep 17 00:00:00 2001 From: mi_sawa Date: Sat, 12 Sep 2020 22:19:32 +0900 Subject: [PATCH 1/7] Add max flow --- atcoder/maxflow.py | 196 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 atcoder/maxflow.py diff --git a/atcoder/maxflow.py b/atcoder/maxflow.py new file mode 100644 index 0000000..e369b67 --- /dev/null +++ b/atcoder/maxflow.py @@ -0,0 +1,196 @@ +from typing import NamedTuple, Optional, List + + +class MaxFlow: + class Edge(NamedTuple): + src: int + dst: int + cap: int + flow: int + + class _Edge: + dst: int + cap: int + rev: Optional['MaxFlow._Edge'] + + def __init__(self, dst: int, cap: int): + self.dst = dst + self.cap = cap + self.rev = None + + def __init__(self, n: int): + self._n = n + self._g: List[List[MaxFlow._Edge]] = [[] for _ in range(n)] + self._edges: List[MaxFlow._Edge] = [] + + def add_edge(self, src: int, dst: int, cap: int) -> int: + assert 0 <= src < self._n + assert 0 <= dst < self._n + assert 0 <= cap + m = len(self._edges) + e = MaxFlow._Edge(dst, cap) + re = MaxFlow._Edge(src, 0) + e.rev = re + re.rev = e + self._g[src].append(e) + self._g[dst].append(re) + self._edges.append(e) + return m + + def get_edge(self, i: int) -> Edge: + assert 0 <= i < len(self._edges) + e = self._edges[i] + re = e.rev + return MaxFlow.Edge( + re.dst, + e.dst, + e.cap + re.cap, + re.cap + ) + + def edges(self) -> List[Edge]: + return [self.get_edge(i) for i in range(len(self._edges))] + + def change_edge(self, i: int, new_cap: int, new_flow: int): + assert 0 <= i < len(self._edges) + assert 0 <= new_flow <= new_cap + e = self._edges[i] + e.cap = new_cap - new_flow + e.rev.cap = new_flow + + def flow(self, s: int, t: int, flow_limit: Optional[int] = None) -> int: + assert 0 <= s < self._n + assert 0 <= t < self._n + assert s != t + if flow_limit is None: + flow_limit = sum(e.cap for e in self._g[s]) + + current_edge = [0] * self._n + level = [0] * self._n + + def fill(arr: List[int], value: int): + for i in range(len(arr)): + arr[i] = value + + def bfs() -> bool: + fill(level, self._n) + queue = [] + q_front = 0 + queue.append(s) + level[s] = 0 + while q_front < len(queue): + v = queue[q_front] + q_front += 1 + next_level = level[v] + 1 + for e in self._g[v]: + if e.cap == 0 or level[e.dst] <= next_level: + continue + level[e.dst] = next_level + if e.dst == t: + return True + queue.append(e.dst) + return False + + def dfs(lim) -> int: + stack = [] + edge_stack = [] + stack.append(t) + while stack: + v = stack[-1] + if v == s: + flow = min(lim, min(e.cap for e in edge_stack)) + for e in edge_stack: + e.cap -= flow + e.rev.cap += flow + return flow + next_level = level[v] - 1 + while current_edge[v] < len(self._g[v]): + e = self._g[v][current_edge[v]] + re = e.rev + if level[e.dst] != next_level or re.cap == 0: + current_edge[v] += 1 + continue + stack.append(e.dst) + edge_stack.append(re) + break + else: + stack.pop() + if edge_stack: + edge_stack.pop() + level[v] = self._n + return 0 + + flow = 0 + while flow < flow_limit: + if not bfs(): + break + fill(current_edge, 0) + while flow < flow_limit: + f = dfs(flow_limit - flow) + flow += f + if f == 0: + break + return flow + + +# https://atcoder.jp/contests/practice2/tasks/practice2_d +def main() -> None: + import sys + n, m = map(int, sys.stdin.readline().split()) + s = n * m + t = s + 1 + g = MaxFlow(t + 1) + grid = [list(sys.stdin.readline().strip()) for _ in range(n)] + + def enc(i: int, j: int) -> int: + return i * m + j + + def dec(v: int) -> (int, int): + return v // m, v % m + + for i in range(n): + for j in range(m): + if grid[i][j] == '#': + continue + if (i + j) % 2 == 0: + g.add_edge(s, enc(i, j), 1) + else: + g.add_edge(enc(i, j), t, 1) + + dx = [1, 0, -1, 0] + dy = [0, 1, 0, -1] + for i in range(n): + for j in range(m): + if (i + j) % 2 == 1 or grid[i][j] == '#': + continue + for direction in range(4): + ii = i + dx[direction] + jj = j + dy[direction] + if 0 <= ii < n and 0 <= jj < m and grid[ii][jj] == '.': + g.add_edge(enc(i, j), enc(ii, jj), 1) + + print(g.flow(s, t)) + for e in g.edges(): + if e.src == s or e.dst == t or e.flow == 0: + continue + (i, j) = dec(e.src) + (ii, jj) = dec(e.dst) + if i == ii + 1: + grid[ii][jj] = 'v' + grid[i][j] = '^' + elif j == jj + 1: + grid[ii][jj] = '>' + grid[i][j] = '<' + elif i == ii - 1: + grid[i][j] = 'v' + grid[ii][jj] = '^' + else: + grid[i][j] = '>' + grid[ii][jj] = '<' + + for s in grid: + print("".join(s)) + + +if __name__ == '__main__': + main() From 1dfca69b7b0920773abb18ddc63c4c58be664b90 Mon Sep 17 00:00:00 2001 From: mi_sawa Date: Sat, 12 Sep 2020 22:29:31 +0900 Subject: [PATCH 2/7] Add min cut --- atcoder/maxflow.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/atcoder/maxflow.py b/atcoder/maxflow.py index e369b67..9c7db1a 100644 --- a/atcoder/maxflow.py +++ b/atcoder/maxflow.py @@ -132,6 +132,19 @@ def dfs(lim) -> int: break return flow + def min_cut(self, s: int) -> List[bool]: + visited = [False] * self._n + stack = [s] + visited[s] = True + while stack: + v = stack.pop() + for e in self._g[v]: + if e.cap > 0 and not visited[e.dst]: + visited[e.dst] = True + stack.append(e.dst) + return visited + + # https://atcoder.jp/contests/practice2/tasks/practice2_d def main() -> None: From 8e1bd87cc722559e7da41da13d76ad7917ea595b Mon Sep 17 00:00:00 2001 From: mi_sawa Date: Sat, 12 Sep 2020 22:58:43 +0900 Subject: [PATCH 3/7] Simplify type hint --- atcoder/maxflow.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/atcoder/maxflow.py b/atcoder/maxflow.py index 9c7db1a..9546bbb 100644 --- a/atcoder/maxflow.py +++ b/atcoder/maxflow.py @@ -9,14 +9,10 @@ class Edge(NamedTuple): flow: int class _Edge: - dst: int - cap: int - rev: Optional['MaxFlow._Edge'] - def __init__(self, dst: int, cap: int): self.dst = dst self.cap = cap - self.rev = None + self.rev: Optional['MaxFlow._Edge'] = None def __init__(self, n: int): self._n = n From d70ad9b9bfb0e37560c3634ef0453733deb63994 Mon Sep 17 00:00:00 2001 From: MiSawa Date: Sun, 13 Sep 2020 13:48:09 +0900 Subject: [PATCH 4/7] Update atcoder/maxflow.py Use key function Co-authored-by: Naoto Mizuno --- atcoder/maxflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atcoder/maxflow.py b/atcoder/maxflow.py index 9546bbb..03d231f 100644 --- a/atcoder/maxflow.py +++ b/atcoder/maxflow.py @@ -94,7 +94,7 @@ def dfs(lim) -> int: while stack: v = stack[-1] if v == s: - flow = min(lim, min(e.cap for e in edge_stack)) + flow = min(lim, min(edge_stack, key=lambda e: e.cap)) for e in edge_stack: e.cap -= flow e.rev.cap += flow From e52d6da3e3e78813c9dd9758cc86dc59c2408ac9 Mon Sep 17 00:00:00 2001 From: mi_sawa Date: Sun, 13 Sep 2020 14:03:58 +0900 Subject: [PATCH 5/7] Use __future__.annotations for self type reference --- atcoder/maxflow.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/atcoder/maxflow.py b/atcoder/maxflow.py index 03d231f..17bbd3f 100644 --- a/atcoder/maxflow.py +++ b/atcoder/maxflow.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing import NamedTuple, Optional, List @@ -12,7 +14,7 @@ class _Edge: def __init__(self, dst: int, cap: int): self.dst = dst self.cap = cap - self.rev: Optional['MaxFlow._Edge'] = None + self.rev: Optional[MaxFlow._Edge] = None def __init__(self, n: int): self._n = n @@ -141,7 +143,6 @@ def min_cut(self, s: int) -> List[bool]: return visited - # https://atcoder.jp/contests/practice2/tasks/practice2_d def main() -> None: import sys From 407796a74bb2401245148f703c4a64138f0fac7c Mon Sep 17 00:00:00 2001 From: MiSawa Date: Sun, 13 Sep 2020 13:47:16 +0900 Subject: [PATCH 6/7] Apply suggestions from code review More type hints Co-authored-by: Naoto Mizuno --- atcoder/maxflow.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/atcoder/maxflow.py b/atcoder/maxflow.py index 17bbd3f..c64437b 100644 --- a/atcoder/maxflow.py +++ b/atcoder/maxflow.py @@ -11,12 +11,12 @@ class Edge(NamedTuple): flow: int class _Edge: - def __init__(self, dst: int, cap: int): + def __init__(self, dst: int, cap: int) -> None: self.dst = dst self.cap = cap self.rev: Optional[MaxFlow._Edge] = None - def __init__(self, n: int): + def __init__(self, n: int) -> None: self._n = n self._g: List[List[MaxFlow._Edge]] = [[] for _ in range(n)] self._edges: List[MaxFlow._Edge] = [] @@ -49,7 +49,7 @@ def get_edge(self, i: int) -> Edge: def edges(self) -> List[Edge]: return [self.get_edge(i) for i in range(len(self._edges))] - def change_edge(self, i: int, new_cap: int, new_flow: int): + def change_edge(self, i: int, new_cap: int, new_flow: int) -> None: assert 0 <= i < len(self._edges) assert 0 <= new_flow <= new_cap e = self._edges[i] @@ -66,7 +66,7 @@ def flow(self, s: int, t: int, flow_limit: Optional[int] = None) -> int: current_edge = [0] * self._n level = [0] * self._n - def fill(arr: List[int], value: int): + def fill(arr: List[int], value: int) -> None: for i in range(len(arr)): arr[i] = value @@ -89,7 +89,7 @@ def bfs() -> bool: queue.append(e.dst) return False - def dfs(lim) -> int: + def dfs(lim: int) -> int: stack = [] edge_stack = [] stack.append(t) From 82f3d482706b119dd431fa53c1fafad15b2bae3d Mon Sep 17 00:00:00 2001 From: mi_sawa Date: Sun, 13 Sep 2020 14:21:48 +0900 Subject: [PATCH 7/7] Fix calculation of bottleneck --- atcoder/maxflow.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/atcoder/maxflow.py b/atcoder/maxflow.py index c64437b..8e49f73 100644 --- a/atcoder/maxflow.py +++ b/atcoder/maxflow.py @@ -3,7 +3,7 @@ from typing import NamedTuple, Optional, List -class MaxFlow: +class MFGraph: class Edge(NamedTuple): src: int dst: int @@ -14,20 +14,20 @@ class _Edge: def __init__(self, dst: int, cap: int) -> None: self.dst = dst self.cap = cap - self.rev: Optional[MaxFlow._Edge] = None + self.rev: Optional[MFGraph._Edge] = None def __init__(self, n: int) -> None: self._n = n - self._g: List[List[MaxFlow._Edge]] = [[] for _ in range(n)] - self._edges: List[MaxFlow._Edge] = [] + self._g: List[List[MFGraph._Edge]] = [[] for _ in range(n)] + self._edges: List[MFGraph._Edge] = [] def add_edge(self, src: int, dst: int, cap: int) -> int: assert 0 <= src < self._n assert 0 <= dst < self._n assert 0 <= cap m = len(self._edges) - e = MaxFlow._Edge(dst, cap) - re = MaxFlow._Edge(src, 0) + e = MFGraph._Edge(dst, cap) + re = MFGraph._Edge(src, 0) e.rev = re re.rev = e self._g[src].append(e) @@ -39,7 +39,7 @@ def get_edge(self, i: int) -> Edge: assert 0 <= i < len(self._edges) e = self._edges[i] re = e.rev - return MaxFlow.Edge( + return MFGraph.Edge( re.dst, e.dst, e.cap + re.cap, @@ -96,7 +96,7 @@ def dfs(lim: int) -> int: while stack: v = stack[-1] if v == s: - flow = min(lim, min(edge_stack, key=lambda e: e.cap)) + flow = min(lim, min(e.cap for e in edge_stack)) for e in edge_stack: e.cap -= flow e.rev.cap += flow @@ -149,7 +149,7 @@ def main() -> None: n, m = map(int, sys.stdin.readline().split()) s = n * m t = s + 1 - g = MaxFlow(t + 1) + g = MFGraph(t + 1) grid = [list(sys.stdin.readline().strip()) for _ in range(n)] def enc(i: int, j: int) -> int: