diff --git a/AtCoderLibrary/MaxFlow.cs b/AtCoderLibrary/MaxFlow.cs new file mode 100644 index 00000000..eb4d87f6 --- /dev/null +++ b/AtCoderLibrary/MaxFlow.cs @@ -0,0 +1,379 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AtCoder +{ + /// + /// 最大フロー問題 を解くライブラリ(int版)です。 + /// + public class MFGraphInt : MFGraph { public MFGraphInt(int n) : base(n) { } } + + /// + /// 最大フロー問題 を解くライブラリ(long版)です。 + /// + public class MFGraphLong : MFGraph { public MFGraphLong(int n) : base(n) { } } + + /// + /// 最大フロー問題 を解くライブラリです。 + /// + /// 容量の型 + /// に対応する演算を提要する型 + /// + /// 制約: は int, long。 + /// + /// 内部では各辺 e について 2 つの変数、流量 f_e と容量 c_e を管理しています。 + /// 頂点 v から出る辺の集合を out(v)、入る辺の集合を in(v)、 + /// また頂点 v について g(v, f) = (Σ_in(v) f_e) - (Σ_out(v) f_e) とします。 + /// + /// + public class MFGraph + where TValue : struct + where TOp : struct, INumOperator + { + static readonly TOp op = default; + + /// + /// 頂点 0 辺のグラフを作ります。 + /// + /// + /// 制約: 0 ≤ ≤ 10^8 + /// 計算量: O() + /// + public MFGraph(int n) + { + _n = n; + _g = new List[n]; + for (int i = 0; i < n; i++) + { + _g[i] = new List(); + } + _pos = new List<(int first, int second)>(); + } + + /// + /// から へ + /// 最大容量 、流量 0 の辺を追加し、何番目に追加された辺かを返します。 + /// + /// + /// 制約: + /// + /// + /// 0 ≤ , < n + /// + /// + /// 0 ≤ + /// + /// + /// 計算量: ならしO(1) + /// + public int AddEdge(int from, int to, TValue cap) + { + int m = _pos.Count; + Debug.Assert(0 <= from && from < _n); + Debug.Assert(0 <= to && to < _n); + Debug.Assert(op.LessThanOrEqual(default, cap)); + _pos.Add((from, _g[from].Count)); + _g[from].Add(new EdgeInternal(to, _g[to].Count, cap)); + _g[to].Add(new EdgeInternal(from, _g[from].Count - 1, default)); + return m; + } + + /// + /// 今の内部の辺の状態を返します。 + /// + /// + /// AddEdge で 番目 (0-indexed) に追加された辺を返す。 + /// 計算量: O(1) + /// + public Edge GetEdge(int i) + { + int m = _pos.Count; + Debug.Assert(0 <= i && i < m); + var _e = _g[_pos[i].first][_pos[i].second]; + var _re = _g[_e.To][_e.Rev]; + return new Edge(_pos[i].first, _e.To, op.Add(_e.Cap, _re.Cap), _re.Cap); + } + + /// + /// 今の内部の辺の状態を返します。 + /// + /// + /// 辺の順番はadd_edgeで追加された順番と同一。 + /// 計算量: O(n) + /// + public List Edges() + { + int m = _pos.Count; + var result = new List(); + for (int i = 0; i < m; i++) + { + result.Add(GetEdge(i)); + } + return result; + } + + /// + /// 番目に追加された辺の容量、流量を + /// , に変更します。 + /// + /// + /// + /// 他の辺の容量、流量は変更しません。 + /// 辺 の流量、容量のみを + /// , へ変更します。 + /// + /// + public void ChangeEdge(int i, TValue newCap, TValue newFlow) + { + int m = _pos.Count; + Debug.Assert(0 <= i && i < m); + Debug.Assert(op.LessThanOrEqual(default, newFlow) && op.LessThanOrEqual(newFlow, newCap)); + var _e = _g[_pos[i].first][_pos[i].second]; + var _re = _g[_e.To][_e.Rev]; + _e.Cap = op.Subtract(newCap, newFlow); + _re.Cap = newFlow; + } + + /// + /// 頂点 から へ流せる限り流し、 + /// 流せた量を返します。 + /// + /// + /// + /// 複数回呼ぶことも可能で、その時の挙動は + /// 変更前と変更後の流量を f_e, f'_e として、以下の条件を満たすように変更します。 + /// + /// + /// + /// 0 ≤ f'_e ≤ C_e + /// + /// + /// + /// , 以外の頂天 v について、 + /// g(v, f) = g(v, f') + /// + /// + /// + /// + /// (flowLimit を指定した場合) g(t, f') - g(t, f) ≤ flowLimit + /// + /// + /// + /// + /// g(t, f') - g(t, f) が委譲の条件を満たすうち最大。この g(t, f') - g(t, f) を返す。 + /// + /// + /// + /// 制約: 返値が に収まる。 + /// 計算量: m を追加された辺数として、 + /// + /// + /// O(min(n^(2/3) m, m^(3/2))) (辺の容量が全部 1 の時) + /// + /// + /// O(n^2 m) + /// + /// + /// + public TValue Flow(int s, int t) + { + return Flow(s, t, op.MaxValue); + } + + /// + /// 頂点 から へ + /// 流量 に達するまで流せる限り流し、 + /// 流せた量を返します。 + /// + /// + /// + /// 複数回呼ぶことも可能で、その時の挙動は + /// 変更前と変更後の流量を f_e, f'_e として、以下の条件を満たすように変更します。 + /// + /// + /// + /// 0 ≤ f'_e ≤ C_e + /// + /// + /// + /// , 以外の頂天 v について、 + /// g(v, f) = g(v, f') + /// + /// + /// + /// + /// ( を指定した場合) g(t, f') - g(t, f) ≤ + /// + /// + /// + /// + /// g(t, f') - g(t, f) が委譲の条件を満たすうち最大。この g(t, f') - g(t, f) を返す。 + /// + /// + /// + /// 制約: 返値が に収まる。 + /// 計算量: m を追加された辺数として、 + /// + /// + /// O(min(n^(2/3) m, m^(3/2))) (辺の容量が全部 1 の時) + /// + /// + /// O(n^2 m) + /// + /// + /// + public TValue Flow(int s, int t, TValue flowLimit) + { + Debug.Assert(0 <= s && s < _n); + Debug.Assert(0 <= t && t < _n); + + var level = new int[_n]; + var iter = new int[_n]; + var que = new Queue(); + + void Bfs() + { + for (int i = 0; i < _n; i++) + { + level[i] = -1; + } + + level[s] = 0; + que.Clear(); + que.Enqueue(s); + while (que.Count > 0) + { + int v = que.Dequeue(); + foreach (var e in _g[v]) + { + if (op.Equals(e.Cap, default) || level[e.To] >= 0) continue; + level[e.To] = level[v] + 1; + if (e.To == t) return; + que.Enqueue(e.To); + } + } + } + + TValue Dfs(int v, TValue up) + { + if (v == s) return up; + var res = default(TValue); + int level_v = level[v]; + for (; iter[v] < _g[v].Count; iter[v]++) + { + EdgeInternal e = _g[v][iter[v]]; + if (level_v <= level[e.To] || op.Equals(_g[e.To][e.Rev].Cap, default)) continue; + var up1 = op.Subtract(up, res); + var up2 = _g[e.To][e.Rev].Cap; + var d = Dfs(e.To, op.LessThan(up1, up2) ? up1 : up2); + if (op.Compare(d, default) <= 0) continue; + _g[v][iter[v]].Cap = op.Add(_g[v][iter[v]].Cap, d); + _g[e.To][e.Rev].Cap = op.Subtract(_g[e.To][e.Rev].Cap, d); + res = op.Add(res, d); + if (res.Equals(up)) break; + } + + return res; + } + + TValue flow = default; + while (op.LessThan(flow, flowLimit)) + { + Bfs(); + if (level[t] == -1) break; + for (int i = 0; i < _n; i++) + { + iter[i] = 0; + } + while (op.LessThan(flow, flowLimit)) + { + var f = Dfs(t, op.Subtract(flowLimit, flow)); + if (op.Equals(f, default)) break; + flow = op.Add(flow, f); + } + } + return flow; + } + + /// + /// 長さ n の配列を返します。 + /// i 番目の要素には、頂点 から i へ残余グラフで到達可能なとき、 + /// またその時のみ true を返します。 + /// Flow(s, t) を flowLimit なしでちょうど一回呼んだ後に呼ぶと、 + /// 返り値はs, t 間の mincut に対応します。 + /// + /// + /// + /// 各辺 e = (u, v, f_e, c_e) について、 f_e < c_e ならば辺 (u, v) を張り、 + /// 0 < f_e ならば辺 (u, v) を張ったと仮定したとき、 + /// 頂点 ss から到達可能な頂点の集合を返します。 + /// + /// 計算量: m を追加された辺数として、 + /// + /// + /// O(n + m) + /// + /// + /// + public bool[] MinCut(int s) + { + var visited = new bool[_n]; + var que = new Queue(); + que.Enqueue(s); + while (que.Count > 0) + { + int p = que.Dequeue(); + visited[p] = true; + foreach (var e in _g[p]) + { + if (!op.Equals(e.Cap, default) && !visited[e.To]) + { + visited[e.To] = true; + que.Enqueue(e.To); + } + } + } + + return visited; + } + + /// + /// フロー流すグラフの各辺に対応した情報を持ちます。 + /// + public struct Edge + { + /// フローが流出する頂点。 + public int From { get; set; } + /// フローが流入する頂点。 + public int To { get; set; } + /// 辺の容量。 + public TValue Cap { get; set; } + /// 辺の流量。 + public TValue Flow { get; set; } + public Edge(int from, int to, TValue cap, TValue flow) + { + From = from; + To = to; + Cap = cap; + Flow = flow; + } + }; + + private class EdgeInternal + { + public int To { get; set; } + public int Rev { get; set; } + public TValue Cap { get; set; } + public EdgeInternal(int to, int rev, TValue cap) + { + To = to; + Rev = rev; + Cap = cap; + } + }; + + private readonly int _n; + private readonly List<(int first, int second)> _pos; + private readonly List[] _g; + } +}