From 54acfbd1f24bf27bb96752dc63f1c4544b13d6c3 Mon Sep 17 00:00:00 2001 From: takytank Date: Sat, 12 Sep 2020 22:48:17 +0900 Subject: [PATCH 1/4] #14 #33 add MaxFlow --- AtCoderLibrary/MaxFlow.cs | 202 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 AtCoderLibrary/MaxFlow.cs diff --git a/AtCoderLibrary/MaxFlow.cs b/AtCoderLibrary/MaxFlow.cs new file mode 100644 index 00000000..1385c511 --- /dev/null +++ b/AtCoderLibrary/MaxFlow.cs @@ -0,0 +1,202 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace AtCoder +{ + public class MFGraphInt : MFGraph { public MFGraphInt(int n) : base(n) { } } + public class MFGraphUInt : MFGraph { public MFGraphUInt(int n) : base(n) { } } + public class MFGraphLong : MFGraph { public MFGraphLong(int n) : base(n) { } } + public class MFGraphULong : MFGraph { public MFGraphULong(int n) : base(n) { } } + public class MFGraphDouble : MFGraph { public MFGraphDouble(int n) : base(n) { } } + public class MFGraph + where TValue : struct + where TOp : struct, INumOperator + { + static readonly TOp op = default; + 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)>(); + } + + 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.Compare(default, cap) <= 0); + _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; + } + + 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); + } + + 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.Compare(default, newFlow) <= 0 && op.Compare(newFlow, newCap) <= 0); + 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; + } + + public TValue Flow(int s, int t) + { + return Flow(s, t, op.MaxValue); + } + + 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.Compare(up1, up2) < 0 ? 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.Compare(flow, flowLimit) < 0) + { + Bfs(); + if (level[t] == -1) break; + for (int i = 0; i < _n; i++) + { + iter[i] = 0; + } + while (op.Compare(flow, flowLimit) < 0) + { + var f = Dfs(t, op.Subtract(flowLimit, flow)); + if (op.Equals(f, default)) break; + flow = op.Add(flow, f); + } + } + return flow; + } + + 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; + } +} From f405d3ec29a9c40c7fad71d2c368b7aca92cb503 Mon Sep 17 00:00:00 2001 From: takytank Date: Sun, 13 Sep 2020 00:22:20 +0900 Subject: [PATCH 2/4] add MinFlow comments --- AtCoderLibrary/MaxFlow.cs | 176 +++++++++++++++++++++++++++++++++++++- 1 file changed, 173 insertions(+), 3 deletions(-) diff --git a/AtCoderLibrary/MaxFlow.cs b/AtCoderLibrary/MaxFlow.cs index 1385c511..13baadf8 100644 --- a/AtCoderLibrary/MaxFlow.cs +++ b/AtCoderLibrary/MaxFlow.cs @@ -3,16 +3,42 @@ namespace AtCoder { + /// + /// 最大フロー問題 を解くライブラリ(int版)です。 + /// public class MFGraphInt : MFGraph { public MFGraphInt(int n) : base(n) { } } - public class MFGraphUInt : MFGraph { public MFGraphUInt(int n) : base(n) { } } + + /// + /// 最大フロー問題 を解くライブラリ(long版)です。 + /// public class MFGraphLong : MFGraph { public MFGraphLong(int n) : base(n) { } } - public class MFGraphULong : MFGraph { public MFGraphULong(int n) : base(n) { } } - public class MFGraphDouble : MFGraph { public MFGraphDouble(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; @@ -24,6 +50,22 @@ public MFGraph(int n) _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; @@ -36,6 +78,13 @@ public int AddEdge(int from, int to, TValue cap) return m; } + /// + /// 今の内部の辺の状態を返します。 + /// + /// + /// AddEdge で 番目 (0-indexed) に追加された辺を返す。 + /// 計算量: O(1) + /// public Edge GetEdge(int i) { int m = _pos.Count; @@ -45,6 +94,13 @@ public Edge GetEdge(int i) 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; @@ -56,6 +112,17 @@ public List Edges() return result; } + /// + /// 番目に追加された辺の容量、流量を + /// , に変更します。 + /// + /// + /// + /// 他の辺の容量、流量は変更しません。 + /// 辺 の流量、容量のみを + /// , へ変更します。 + /// + /// public void ChangeEdge(int i, TValue newCap, TValue newFlow) { int m = _pos.Count; @@ -67,11 +134,94 @@ public void ChangeEdge(int i, TValue newCap, TValue 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); @@ -145,6 +295,26 @@ TValue Dfs(int v, TValue up) 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]; From 479482c4e258c0051f282f42e00bd9bc4ba1d570 Mon Sep 17 00:00:00 2001 From: takytank Date: Sun, 13 Sep 2020 00:32:22 +0900 Subject: [PATCH 3/4] use comparison operators --- AtCoderLibrary/MaxFlow.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/AtCoderLibrary/MaxFlow.cs b/AtCoderLibrary/MaxFlow.cs index 13baadf8..96d74033 100644 --- a/AtCoderLibrary/MaxFlow.cs +++ b/AtCoderLibrary/MaxFlow.cs @@ -71,7 +71,7 @@ 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.Compare(default, cap) <= 0); + 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)); @@ -127,7 +127,7 @@ public void ChangeEdge(int i, TValue newCap, TValue newFlow) { int m = _pos.Count; Debug.Assert(0 <= i && i < m); - Debug.Assert(op.Compare(default, newFlow) <= 0 && op.Compare(newFlow, newCap) <= 0); + 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); @@ -265,7 +265,7 @@ TValue Dfs(int v, TValue up) 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.Compare(up1, up2) < 0 ? up1 : up2); + 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); @@ -277,7 +277,7 @@ TValue Dfs(int v, TValue up) } TValue flow = default; - while (op.Compare(flow, flowLimit) < 0) + while (op.LessThan(flow, flowLimit)) { Bfs(); if (level[t] == -1) break; @@ -285,7 +285,7 @@ TValue Dfs(int v, TValue up) { iter[i] = 0; } - while (op.Compare(flow, flowLimit) < 0) + while (op.LessThan(flow, flowLimit)) { var f = Dfs(t, op.Subtract(flowLimit, flow)); if (op.Equals(f, default)) break; From 4609d58f8b2a44c1e635a2d0a00ed0886cd776d5 Mon Sep 17 00:00:00 2001 From: takytank Date: Sun, 13 Sep 2020 00:47:07 +0900 Subject: [PATCH 4/4] add Edge comments --- AtCoderLibrary/MaxFlow.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/AtCoderLibrary/MaxFlow.cs b/AtCoderLibrary/MaxFlow.cs index 96d74033..eb4d87f6 100644 --- a/AtCoderLibrary/MaxFlow.cs +++ b/AtCoderLibrary/MaxFlow.cs @@ -337,11 +337,18 @@ public bool[] MinCut(int s) 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) {