acl_cpp.maxflow
maxflow.mf_graph
# mf_graph<ll>
from acl_cpp.maxflow import mf_graph
最大フロー問題 を解くライブラリです。
コンストラクタ
# mf_graph::mf_graph(int n)
def mf_graph(n: int = 0) -> mf_graph
\(n\) 頂点 \(0\) 辺のグラフを作ります。
制約
\(0 \le n < 2^{31}\)
計算量
\(O(n)\)
mf_graph.add_edge
# int mf_graph::add_edge(int from, int to, ll cap)
def mf_graph.add_edge(from_: int, to: int, cap: int) -> int
頂点 \(\mathit{from\_}\) から頂点 \(\mathit{to}\) へ最大容量 \(\mathit{cap}\) 流量 \(0\) の辺を追加します。何番目に追加された辺かを返します (0-origin) 。
制約
\(0 \le \mathit{from\_} < n\)
\(0 \le \mathit{to} < n\)
\(0 \le \mathit{cap} < 2^{63}\)
計算量
ならし \(O(1)\)
mf_graph.flow
# (1) ll mf_graph::flow(int s, int t)
def mf_graph.flow(s: int, t: int) -> int
# (2) ll mf_graph::flow(int s, int t, ll flow_limit)
def mf_graph.flow(s: int, t: int, flow_limit: int) -> int
(1)
flow(s, t, (1 << 63) - 1)
と等価です。(2) 頂点 \(s\) から頂点 \(t\) へ流量 \(\mathit{flow\_limit}\) に達するまで流せる限り流し、流せた量を返します。
複数回呼ぶことも可能です。詳細な挙動は Appendix を参照してください。
制約
\(0 \le s < n\)
\(0 \le t < n\)
\(s \neq t\)
\(0 \le \mathit{flow\_limit} < 2^{63}\)
計算量
\(m\) を追加された辺数として
\(O((n + m) \sqrt{m})\) (辺の容量がすべて \(1\) のとき)
\(O(n^2 m)\)
返り値を \(F\) として \(O(F(n + m))\)
参考文献: Dinic 法とその計算量
mf_graph.min_cut
# vector<bool> mf_graph::min_cut(int s)
def mf_graph.min_cut(s: int) -> list[bool]
以下で定める長さ \(n\) の bool の配列 cut
を返します。
cut[i]
は、現在の残余グラフにおいて、頂点 \(s\) から頂点 \(i\) へ到達可能かどうか
flow(s, t)
をちょうど \(1\) 回呼んだ後に min_cut(s)
を呼ぶと、返り値は \(s\)-\(t\) 間の最小カットに対応します。詳細な挙動は Appendix を参照してください。
mf_graph.edge
mf_graph 上の辺を表す class です。read-only な変数
from_
,to
,cap
,flow
からなります。cap
は最大容量、flow
は現在の流量を表します。from
は予約語なのでfrom_
としています。
コンストラクタ
# mf_graph::edge::edge(int from, int to, ll cap, ll flow)
def mf_graph.edge(from_: int, to: int, cap: int, flow: int) -> mf_graph.edge
mf_graph.get_edge
# mf_graph::edge mf_graph::get_edge(int i)
def mf_graph.get_edge(i: int) -> mf_graph.edge
\(i\) 番目に追加された辺の現在の状態を返します。
制約
\(m\) をこれまでに追加された辺の数として
\(0 \le i < m\)
計算量
\(O(1)\)
mf_graph.edges
# vector<mf_graph::edge> mf_graph::edges()
def mf_graph.edges() -> list[mf_graph.edge]
すべての辺の現在の状態を返します。辺は追加された順に並びます。
計算量
\(O(m)\)
mf_graph.change_edge
# void mf_graph::change_edge(int i, ll new_cap, ll new_flow)
def mf_graph.change_edge(i: int, new_cap: int, new_flow: int) -> None
\(i\) 番目 (0-origin) に追加された辺の最大容量を \(\mathit{new\_cap}\) 、流量を \(\mathit{new\_flow}\) に変更します。詳細は Appendix を参照してください。
制約
\(0 \le i < m\)
\(0 \le \mathit{new\_flow} \le \mathit{new\_cap} < 2^{63}\)
使用例
AC code of https://atcoder.jp/contests/practice2/tasks/practice2_d
from acl_cpp.maxflow import mf_graph
HASH = ord('#')
DOT = ord('.')
N, M = map(int, input().split())
grid = [bytearray(input().encode()) for _ in range(N)]
s = N * M
t = s + 1
g = mf_graph(N * M + 2)
# s -> even / odd -> t
for i in range(N):
for j in range(M):
if grid[i][j] == HASH:
continue
v = i * M + j
if (i + j) % 2 == 0:
g.add_edge(s, v, 1)
else:
g.add_edge(v, t, 1)
# even -> odd
for i in range(N):
for j in range(M):
if (i + j) % 2 or grid[i][j] == HASH:
continue
v0 = i * M + j
if i > 0 and grid[i - 1][j] == DOT:
g.add_edge(v0, v0 - M, 1)
if i + 1 < N and grid[i + 1][j] == DOT:
g.add_edge(v0, v0 + M, 1)
if j > 0 and grid[i][j - 1] == DOT:
g.add_edge(v0, v0 - 1, 1)
if j + 1 < M and grid[i][j + 1] == DOT:
g.add_edge(v0, v0 + 1, 1)
print(g.flow(s, t))
for e in g.edges():
if e.from_ == s or e.to == t or e.flow == 0:
continue
i0, j0 = divmod(e.from_, M)
i1, j1 = divmod(e.to, M)
if i0 + 1 == i1:
grid[i0][j0] = ord('v')
grid[i1][j1] = ord('^')
elif i1 + 1 == i0:
grid[i1][j1] = ord('v')
grid[i0][j0] = ord('^')
elif j0 + 1 == j1:
grid[i0][j0] = ord('>')
grid[i1][j1] = ord('<')
else:
grid[i1][j1] = ord('>')
grid[i0][j0] = ord('<')
for row in grid:
print(row.decode())