From adcfa5cc51e751b70954c69b1c80912f8dbbc80d Mon Sep 17 00:00:00 2001 From: mban Date: Mon, 13 May 2024 17:47:08 +0900 Subject: [PATCH] =?UTF-8?q?mincostflow=E8=BF=BD=E5=8A=A0=20=E5=AE=9F?= =?UTF-8?q?=E8=A3=85=E9=80=94=E4=B8=AD=20=E3=82=A8=E3=83=A9=E3=83=BC?= =?UTF-8?q?=E5=87=BA=E3=81=BE=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/graph/MinCostFlow.kt | 138 +++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 src/main/kotlin/graph/MinCostFlow.kt diff --git a/src/main/kotlin/graph/MinCostFlow.kt b/src/main/kotlin/graph/MinCostFlow.kt new file mode 100644 index 0000000..99bf67c --- /dev/null +++ b/src/main/kotlin/graph/MinCostFlow.kt @@ -0,0 +1,138 @@ +package graph + +typealias Capacity = Long +typealias Cost = Long + +/** + * Minimum-cost flow problemを扱うライブラリです。 + * + * [AC Library](https://atcoder.github.io/ac-library/production/document_ja/mincostflow.html) + */ +class MinCostFlow(private val n: Int) { + private val _edges = mutableListOf() + + /** + * [from]から[to]へ最大容量[cap], コスト[cost]の辺を追加する。何番目に追加された辺かを返す。 + * + * 0 <= [from], [to] < n + * + * 0 <= [cap], [cost] + */ + public fun addEdge(from: Int, to: Int, cap: Capacity, cost: Cost): Int { + assert(from in 0.. = flow(s, t, Capacity.MAX_VALUE) + + /** + * [s]から[t]へ流量[flowLimit]まで流せるだけ流し、その流量とコストを返す。 + * @return (流量,コスト)のPair + */ + public fun flow(s: Int, t: Int, flowLimit: Capacity): Pair = slope(s, t, flowLimit).last() + + /** + * 返り値に流量とコストの関係の折れ線が入る。全てのxについて、流量xのときの最小コストをg(x)とすると、(x,g(x))は返り値を折れ線として見たものに含まれる。 + * * 返り値の最初の要素は(0,0) + * * 返り値の.first、.secondは共に狭義単調増加 + * * 3点が同一線上にあることはない + * * 返り値の最後の要素は最大流量xとして(x,g(x)) + */ + public fun slope(s: Int, t: Int): List> = slope(s, t, Capacity.MAX_VALUE) + + /** + * 返り値に流量とコストの関係の折れ線が入る。全てのxについて、流量xのときの最小コストをg(x)とすると、(x,g(x))は返り値を折れ線として見たものに含まれる。 + * * 返り値の最初の要素は(0,0) + * * 返り値の.first、.secondは共に狭義単調増加 + * * 3点が同一線上にあることはない + * * 返り値の最後の要素はy = min(x, [flowLimit])として(y,g(y)) + */ + public fun slope(s: Int, t: Int, flowLimit: Capacity): List> { + assert(s in 0..>(2 * m) + for (i in _edges.indices) { + val e = _edges[i] + edgeIdx[i] = degree[e.from]++ + redgeIdx[i] = degree[e.to]++ + elist.add(e.from to InternalEdge(e.to, -1, e.cap - e.flow, e.cost)) + elist.add(e.to to InternalEdge(e.from, -1, e.flow, -e.cost)) + } + val g = CSR(n, elist) + for (i in _edges.indices) { + val e = _edges[i] + edgeIdx[i] += g.start[e.from] + edgeIdx[i] += g.start[e.to] + g.elist[edgeIdx[i]].rev = redgeIdx[i] + g.elist[redgeIdx[i]].rev = edgeIdx[i] + } + + val result = slope(g, s, t, flowLimit) + + for (i in _edges.indices) { + val e = g.elist[edgeIdx[i]] + _edges[i].flow = _edges[i].cap - e.cap + } + + return result + } + + private fun slope(g: CSR, s: Int, t: Int, flowLimit: Capacity): List> { + val dualDist = Array(n) { MutablePairCostCost(-1, -1) } + val prevE = IntArray(n) { 0 } + val vis = BooleanArray(n) { false } + + data class Q(val key: Cost, val to: Int) { + operator fun compareTo(r: Q) = -key.compareTo(r.key) + } + + val queMin = ArrayDeque() + val que = ArrayDeque() + + fun dualRef() { + for (i in 0..>) { + val start = IntArray(n + 1) { 0 } + val elist = Array(edges.size) { InternalEdge(-1, -1, -1, -1) } + + init { + for (e in edges) { + start[e.first + 1]++ + } + for (i in 1..n) { + start[i] += start[i - 1] + } + val counter = start.clone() + for (e in edges) { + elist[counter[e.first]++] = e.second + } + } + } +} \ No newline at end of file