From 5dd3347e19222d80706cb16b4aa9b544039d682b Mon Sep 17 00:00:00 2001 From: kekeandzeyu Date: Tue, 5 Nov 2024 13:34:04 +0800 Subject: [PATCH] fixing indentation problem caused by converting markdown to xml files --- .../Data-Structures-and-Algorithms-3.topic | 2009 +++++++---------- 1 file changed, 852 insertions(+), 1157 deletions(-) diff --git a/Writerside/topics/Data-Structures-and-Algorithms-3.topic b/Writerside/topics/Data-Structures-and-Algorithms-3.topic index 1979cf7..7053e07 100644 --- a/Writerside/topics/Data-Structures-and-Algorithms-3.topic +++ b/Writerside/topics/Data-Structures-and-Algorithms-3.topic @@ -2801,101 +2801,33 @@ -

- Definitions -

-

- - st - -cut: - - A - - st - -cut (cut) - - is a - partition of the vertices into two disjoint sets, with - s - - in one set - A - and - t - in the other - set - B - . -

-

- - st - -cut capacity: - - Its - capacity - is the sum of the - capacities of the edges from - A - to - B - . -

+

Definitions

+

st-cut: A st-cut + (cut) is a partition of the vertices into two disjoint sets, with s in one set + A and t in the other set B.

+

st-cut capacity: Its capacity + is the sum of the capacities of the edges from A to B.

Each edge has a positive capacity in edge-weighted digraph here.

st-cut -

- Minimum cut problem: - Find a cut of minimum capacity. -

-

- Definitions -

-

- - st - -flow: - - An - - st - -flow (flow) - - is - an assignment of values to the edges such that: -

+

Minimum cut problem: Find a cut of minimum capacity.

+

Definitions

+

st-flow: An st + -flow (flow) is an assignment of values to the edges such that:

  • Capacity constraint: 0 ≤ edge's flow ≤ edge's capacity.

  • -

    Local equilibrium: inflow = outflow at every vertex (except - s - - and - t - ). -

    +

    Local equilibrium: inflow = outflow at every vertex (except s and t + ).

  • st-flow -

    - Value of a flow: - The value of a flow is the inflow at - t - (assuming - no edge points to - s - or from - t - . -

    -

    - Maximum st-flow (maxflow) problem: - - Find a flow of maximum value. -

    +

    Value of a flow: The value of a flow is the inflow at t + (assuming no edge points to s or from t).

    +

    Maximum st-flow (maxflow) problem: Find a flow of maximum value.

    These two problems are dual!

    @@ -2911,118 +2843,40 @@

    2. Can decrease flow on backward edges (not empty).

    -

    Terminates when all paths from s to t are blocked by either a - full forward edge or an empty backward edge.

    +

    Terminates when all paths from s to t are blocked by either a full forward edge or an empty + backward edge.

    - Ford-Fulkerson AlgorithmFord-Fulkerson Algorithm + Ford-Fulkerson AlgorithmFord-Fulkerson Algorithm
    -

    - Definition -

    -

    - Net Flow: - The - net flow across - - a cut ( - A - , - B - - ) is the sum of the flows on its edges from - A - to - B - minus the sum of the flows on its edges from from - B - to - A - . -

    -

    - Flow-value lemma: - Let - f - - be any flow and let ( - A - , - B - ) be any - cut. Then, the net flow across ( - A - , - B - ) - equals the value of - f - . -

    -

    - Proof: - By induction on the size of - B - . -

    +

    Definition

    +

    Net Flow: The net flow across a cut + (A, B) is the sum of the flows on its edges from A to B + minus the sum of the flows on its edges from from B to A).

    +

    Flow-value lemma: Let f be any flow and let (A + , B) be any cut. Then, the net flow across (A, B) equals + the value of f.

    +

    Proof: By induction on the size of B.

  • -

    Base case: - B = {t} -

    +

    Base case: B = {t}

  • -

    Induction step: remains true by local equilibrium when moving - any vertex from - A - to - B - . -

    +

    Induction step: remains true by local equilibrium when moving any vertex from A to + B.

  • -

    - Weak duality: - Let - f - - be any flow and let - (A, B) - be any cut. Then, the - value of the flow ≤ the capacity of the cut. -

    -

    - Proof -

    -

    Value of flow - f - = net flow across cut - (A, B) - - ≤ capacity of cut - (A, B) - . -

    -

    - Augmenting path theorem: - A - flow f is a maxflow iff no augmenting paths. -

    -

    - Maxflow-mincut theorem: - Value - of the maxflow = capacity of mincut. -

    -

    - Proof: - The following three - conditions are equivalent for any flow - f - . -

    +

    Weak duality: Let f be any flow and let (A, B) + be any cut. Then, the value of the flow ≤ the capacity of the cut.

    +

    Proof

    +

    Value of flow f = net flow across cut (A, B) ≤ capacity of cut (A, B) + .

    +

    Augmenting path theorem: A flow f is a maxflow iff no augmenting + paths.

    +

    Maxflow-mincut theorem: Value of the maxflow = capacity of mincut.

    +

    Proof: The following three conditions are equivalent for any flow + f.

  • There exists a cut whose capacity equals the value of the flow @@ -3043,187 +2897,91 @@

  • -

    - 1 -> 2 -

    +

    1 -> 2

  • -

    Suppose that - (A, B) - is a cut with capacity equal - to the value of - f - . -

    +

    Suppose that (A, B) is a cut with capacity equal to the value of f.

  • -

    Then, the value of any flow - f' - ≤ capacity of - (A, B) - = value of - f - . -

    +

    Then, the value of any flow f' ≤ capacity of (A, B) = value of f + .

  • -

    Thus, - f - is a maxflow. -

    +

    Thus, f is a maxflow.

  • -

    - 2 -> 3: - We prove - contrapositive: ~3 -> ~2 -

    +

    2 -> 3: We prove contrapositive: ~3 -> ~2

  • -

    Suppose that there is an augmenting path with respect to - f - . -

    +

    Suppose that there is an augmenting path with respect to f.

  • -

    Can improve flow - f - by sending flow along this path. -

    +

    Can improve flow f by sending flow along this path.

  • Thus, f is not a maxflow.

  • -

    - 3 -> 1: - Suppose that there is no - augmenting path with respect to - f - . -

    - +

    3 -> 1: Suppose that there is no augmenting path with respect to + f.

    +
  • -

    Let - (A, B) - be a cut where - A - is the set - of vertices connected to - s - by an undirected path with - no full forward or empty backward edges. -

    +

    Let (A, B) be a cut where A is the set of vertices connected to + s by an undirected path with no full forward or empty backward edges.

  • -

    By definition, - s - is in - A - ; since no - augmenting path, - t - is in - B - . -

    +

    By definition, s is in A; since no augmenting path, t + is in B.

  • -

    Capacity of cut = net flow across cut (forward edges full; - backward edges empty) = value of flow - f - ( - flow-value lemma). -

    +

    Capacity of cut = net flow across cut (forward edges full; backward edges empty) = value of flow + f (flow-value lemma).

  • -

    To compute mincut - (A, B) - from maxflow - f - : -

    +

    To compute mincut (A, B) from maxflow f:

  • -

    By augmenting path theorem, no augmenting paths with respect - to - f - . -

    +

    By augmenting path theorem, no augmenting paths with respect to f.

  • -

    Compute - A - = set of vertices connected to - s - - by an undirected path with no full forward or empty - backward edges. -

    +

    Compute A = set of vertices connected to s by an undirected path with \ + no full forward or empty backward edges.

  • Compute Mincut
    -

    Important special case: Edge capacities are integers between 1 and - U - . -

    +

    Important special case: Edge capacities are integers between 1 and U.

    -

    - Properties -

    +

    Properties

  • The flow is integer-valued throughout Ford-Fulkerson.

    -

    - Proof -

    +

    Proof

  • Bottleneck capacity is an integer.

  • -

    Flow on an edge increases/decreases by bottleneck capacity. -

    +

    Flow on an edge increases/decreases by bottleneck capacity.

  • Number of augmentations ≤ the value of the maxflow.

    -

    - Proof: - Each augmentation - increases the value by at least 1. +

    Proof: Each augmentation increases the value by at least 1.

  • -

    - Integrality theorem: - There - exists an integer-valued maxflow. -

    -

    - Proof: - Ford-Fulkerson - terminates and maxflow that it finds is integer-valued. -

    +

    Integrality theorem: There exists an integer-valued maxflow.

    +

    Proof: Ford-Fulkerson terminates and maxflow that it finds is + integer-valued.

  • -

    - Running time: - FF performance - depends on choice of augmenting paths. -

    -

    Digraph with - V - vertices, - E - edges, and - integer capacities between 1 and - U -

    +

    Running time: FF performance depends on choice of augmenting paths.

    +

    Digraph with V vertices, E edges, and integer capacities between 1 and U +

    @@ -3262,22 +3020,14 @@ -

    - Implementation -

    +

    Implementation

    Use residual capcity:

  • -

    Forward edge: residual capacity - = c_{e} - f_{e} - . -

    +

    Forward edge: residual capacity = c_{e} - f_{e}.

  • -

    Backward edge: residual capacity - = f_{e} - . -

    +

    Backward edge: residual capacity = f_{e}.

  • Flow Edge @@ -3285,95 +3035,97 @@ public class FlowEdge { - private static final double FLOATING_POINT_EPSILON = 1.0E-10; - - private final int v; - private final int w; - private final double capacity; - private double flow; - - public FlowEdge(int v, int w, double capacity) { - if (v < 0) throw new IllegalArgumentException("vertex index must be a non-negative - integer"); - if (w < 0) throw new IllegalArgumentException("vertex index must be a non-negative - integer"); - if (!(capacity >= 0.0)) throw new IllegalArgumentException("Edge capacity must be - non-negative"); - this.v = v; - this.w = w; - this.capacity = capacity; - this.flow = 0.0; - } - - public FlowEdge(int v, int w, double capacity, double flow) { - if (v < 0) throw new IllegalArgumentException("vertex index must be a non-negative - integer"); - if (w < 0) throw new IllegalArgumentException("vertex index must be a non-negative - integer"); - if (!(capacity >= 0.0)) throw new IllegalArgumentException("edge capacity must be - non-negative"); - if (!(flow <= capacity)) throw new IllegalArgumentException("flow exceeds capacity"); - if (!(flow >= 0.0)) throw new IllegalArgumentException("flow must be non-negative"); - this.v = v; - this.w = w; - this.capacity = capacity; - this.flow = flow; - } - - public FlowEdge(FlowEdge e) { - this.v = e.v; - this.w = e.w; - this.capacity = e.capacity; - this.flow = e.flow; - } - - public int from() { - return v; - } + private static final double FLOATING_POINT_EPSILON = 1.0E-10; + + private final int v; + private final int w; + private final double capacity; + private double flow; + + public FlowEdge(int v, int w, double capacity) { + if (v < 0) + throw new IllegalArgumentException("vertex index must be a non-negative integer"); + if (w < 0) + throw new IllegalArgumentException("vertex index must be a non-negative integer"); + if (!(capacity >= 0.0)) + throw new IllegalArgumentException("Edge capacity must be non-negative"); + this.v = v; + this.w = w; + this.capacity = capacity; + this.flow = 0.0; + } - public int to() { - return w; - } + public FlowEdge(int v, int w, double capacity, double flow) { + if (v < 0) + throw new IllegalArgumentException("vertex index must be a non-negative integer"); + if (w < 0) + throw new IllegalArgumentException("vertex index must be a non-negative integer"); + if (!(capacity >= 0.0)) + throw new IllegalArgumentException("edge capacity must be non-negative"); + if (!(flow <= capacity)) + throw new IllegalArgumentException("flow exceeds capacity"); + if (!(flow >= 0.0)) + throw new IllegalArgumentException("flow must be non-negative"); + this.v = v; + this.w = w; + this.capacity = capacity; + this.flow = flow; + } - public double capacity() { - return capacity; - } + public FlowEdge(FlowEdge e) { + this.v = e.v; + this.w = e.w; + this.capacity = e.capacity; + this.flow = e.flow; + } - public double flow() { - return flow; - } + public int from() { + return v; + } - public int other(int vertex) { - if (vertex == v) return w; - else if (vertex == w) return v; - else throw new IllegalArgumentException("invalid endpoint"); - } + public int to() { + return w; + } - public double residualCapacityTo(int vertex) { - if (vertex == v) return flow; - else if (vertex == w) return capacity - flow; - else throw new IllegalArgumentException("invalid endpoint"); - } + public double capacity() { + return capacity; + } - public void addResidualFlowTo(int vertex, double delta) { - if (!(delta >= 0.0)) throw new IllegalArgumentException("Delta must be non-negative"); + public double flow() { + return flow; + } - if (vertex == v) flow -= delta; - else if (vertex == w) flow += delta; - else throw new IllegalArgumentException("invalid endpoint"); + public int other(int vertex) { + if (vertex == v) return w; + else if (vertex == w) return v; + else throw new IllegalArgumentException("invalid endpoint"); + } - if (Math.abs(flow) <= FLOATING_POINT_EPSILON) - flow = 0; - if (Math.abs(flow - capacity) <= FLOATING_POINT_EPSILON) - flow = capacity; + public double residualCapacityTo(int vertex) { + if (vertex == v) return flow; + else if (vertex == w) return capacity - flow; + else throw new IllegalArgumentException("invalid endpoint"); + } - if (!(flow >= 0.0)) throw new IllegalArgumentException("Flow is negative"); - if (!(flow <= capacity)) throw new IllegalArgumentException("Flow exceeds capacity"); - } + public void addResidualFlowTo(int vertex, double delta) { + if (!(delta >= 0.0)) throw new IllegalArgumentException("Delta must be non-negative"); + + if (vertex == v) flow -= delta; + else if (vertex == w) flow += delta; + else throw new IllegalArgumentException("invalid endpoint"); + + if (Math.abs(flow) <= FLOATING_POINT_EPSILON) + flow = 0; + if (Math.abs(flow - capacity) <= FLOATING_POINT_EPSILON) + flow = capacity; + + if (!(flow >= 0.0)) throw new IllegalArgumentException("Flow is negative"); + if (!(flow <= capacity)) throw new IllegalArgumentException("Flow exceeds capacity"); + } - public String toString() { - return v + "->" + w + " " + flow + "/" + capacity; - } + public String toString() { + return v + "->" + w + " " + flow + "/" + capacity; + } } @@ -3386,27 +3138,27 @@ class FlowEdge { private: - static constexpr double FLOATING_POINT_EPSILON = 1.0E-10; + static constexpr double FLOATING_POINT_EPSILON = 1.0E-10; - int v; - int w; - double capacity; - double flow; + int v; + int w; + double capacity; + double flow; public: - FlowEdge(int v, int w, double capacity); - FlowEdge(int v, int w, double capacity, double flow); - FlowEdge(const FlowEdge& e); - - [[nodiscard]] int from() const; - [[nodiscard]] int to() const; - [[nodiscard]] double getcapacity() const; - [[nodiscard]] double getflow() const; - [[nodiscard]] int other(int vertex) const; - [[nodiscard]] double residualCapacityTo(int vertex) const; - void addResidualFlowTo(int vertex, double delta); - - friend std::ostream& operator<<(std::ostream& os, const FlowEdge& e); + FlowEdge(int v, int w, double capacity); + FlowEdge(int v, int w, double capacity, double flow); + FlowEdge(const FlowEdge& e); + + [[nodiscard]] int from() const; + [[nodiscard]] int to() const; + [[nodiscard]] double getcapacity() const; + [[nodiscard]] double getflow() const; + [[nodiscard]] int other(int vertex) const; + [[nodiscard]] double residualCapacityTo(int vertex) const; + void addResidualFlowTo(int vertex, double delta); + + friend std::ostream& operator<<(std::ostream& os, const FlowEdge& e); }; #endif // FLOWEDGE_H @@ -3420,148 +3172,147 @@ FlowEdge::FlowEdge(const int v, const int w, const double capacity) : v(v), w(w), capacity(capacity), flow(0.0) { - if (v < 0) throw std::invalid_argument("vertex index must be a non-negative integer"); - if (w < 0) throw std::invalid_argument("vertex index must be a non-negative integer"); - if (!(capacity >= 0.0)) throw std::invalid_argument("Edge capacity must be - non-negative"); + if (v < 0) throw std::invalid_argument("vertex index must be a non-negative integer"); + if (w < 0) throw std::invalid_argument("vertex index must be a non-negative integer"); + if (!(capacity >= 0.0)) throw std::invalid_argument("Edge capacity must be non-negative"); } FlowEdge::FlowEdge(const int v, const int w, const double capacity, const double flow) : v(v), w(w), capacity(capacity), flow(flow) { - if (v < 0) throw std::invalid_argument("vertex index must be a non-negative integer"); - if (w < 0) throw std::invalid_argument("vertex index must be a non-negative integer"); - if (!(capacity >= 0.0)) throw std::invalid_argument("edge capacity must be - non-negative"); - if (!(flow <= capacity)) throw std::invalid_argument("flow exceeds capacity"); - if (!(flow >= 0.0)) throw std::invalid_argument("flow must be non-negative"); + if (v < 0) throw std::invalid_argument("vertex index must be a non-negative integer"); + if (w < 0) throw std::invalid_argument("vertex index must be a non-negative integer"); + if (!(capacity >= 0.0)) throw std::invalid_argument("edge capacity must be + non-negative"); + if (!(flow <= capacity)) throw std::invalid_argument("flow exceeds capacity"); + if (!(flow >= 0.0)) throw std::invalid_argument("flow must be non-negative"); } FlowEdge::FlowEdge(const FlowEdge& e) = default; int FlowEdge::from() const { - return v; + return v; } int FlowEdge::to() const { - return w; + return w; } double FlowEdge::getcapacity() const { - return capacity; + return capacity; } double FlowEdge::getflow() const { - return flow; + return flow; } int FlowEdge::other(const int vertex) const { - if (vertex == v) return w; - else if (vertex == w) return v; - else throw std::invalid_argument("invalid endpoint"); + if (vertex == v) return w; + else if (vertex == w) return v; + else throw std::invalid_argument("invalid endpoint"); } double FlowEdge::residualCapacityTo(const int vertex) const { - if (vertex == v) return flow; - else if (vertex == w) return capacity - flow; - else throw std::invalid_argument("invalid endpoint"); + if (vertex == v) return flow; + else if (vertex == w) return capacity - flow; + else throw std::invalid_argument("invalid endpoint"); } void FlowEdge::addResidualFlowTo(const int vertex, const double delta) { - if (!(delta >= 0.0)) throw std::invalid_argument("Delta must be non-negative"); - - if (vertex == v) flow -= delta; - else if (vertex == w) flow += delta; - else throw std::invalid_argument("invalid endpoint"); - - if (std::abs(flow) <= FLOATING_POINT_EPSILON) - flow = 0; - if (std::abs(flow - capacity) <= FLOATING_POINT_EPSILON) - flow = capacity; - - if (!(flow >= 0.0)) throw std::invalid_argument("Flow is negative"); - if (!(flow <= capacity)) throw std::invalid_argument("Flow exceeds capacity"); + if (!(delta >= 0.0)) throw std::invalid_argument("Delta must be non-negative"); + + if (vertex == v) flow -= delta; + else if (vertex == w) flow += delta; + else throw std::invalid_argument("invalid endpoint"); + + if (std::abs(flow) <= FLOATING_POINT_EPSILON) + flow = 0; + if (std::abs(flow - capacity) <= FLOATING_POINT_EPSILON) + flow = capacity; + + if (!(flow >= 0.0)) throw std::invalid_argument("Flow is negative"); + if (!(flow <= capacity)) throw std::invalid_argument("Flow exceeds capacity"); } std::ostream& operator<<(std::ostream& os, const FlowEdge& e) { - os << e.v << "->" << e.w << " " << e.flow << "/" << - e.capacity; - return os; + os << e.v << "->" << e.w << " " << e.flow << "/" << + e.capacity; + return os; } class FlowEdge: - FLOATING_POINT_EPSILON = 1e-10 - - def __init__(self, v, w, capacity, flow=0.0): - if v < 0: - raise ValueError("vertex index must be a non-negative integer") - if w < 0: - raise ValueError("vertex index must be a non-negative integer") - if capacity < 0.0: - raise ValueError("Edge capacity must be non-negative") - if flow > capacity: - raise ValueError("flow exceeds capacity") - if flow < 0.0: - raise ValueError("flow must be non-negative") - - self._v = v - self._w = w - self._capacity = capacity - self._flow = flow - - def from_(self): - return self._v - - def to(self): - return self._w - - def capacity(self): - return self._capacity - - def flow(self): - return self._flow - - def other(self, vertex): - if vertex == self._v: - return self._w - elif vertex == self._w: - return self._v - else: - raise ValueError("invalid endpoint") - - def residualCapacityTo(self, vertex): - if vertex == self._v: - return self._flow - elif vertex == self._w: - return self._capacity - self._flow - else: - raise ValueError("invalid endpoint") - - def addResidualFlowTo(self, vertex, delta): - if delta < 0.0: - raise ValueError("Delta must be non-negative") - - if vertex == self._v: - self._flow -= delta - elif vertex == self._w: - self._flow += delta - else: - raise ValueError("invalid endpoint") - - if abs(self._flow) <= self.FLOATING_POINT_EPSILON: - self._flow = 0 - if abs(self._flow - self._capacity) <= self.FLOATING_POINT_EPSILON: - self._flow = self._capacity - - if self._flow < 0.0: - raise ValueError("Flow is negative") - if self._flow > self._capacity: - raise ValueError("Flow exceeds capacity") + FLOATING_POINT_EPSILON = 1e-10 + + def __init__(self, v, w, capacity, flow=0.0): + if v < 0: + raise ValueError("vertex index must be a non-negative integer") + if w < 0: + raise ValueError("vertex index must be a non-negative integer") + if capacity < 0.0: + raise ValueError("Edge capacity must be non-negative") + if flow > capacity: + raise ValueError("flow exceeds capacity") + if flow < 0.0: + raise ValueError("flow must be non-negative") + + self._v = v + self._w = w + self._capacity = capacity + self._flow = flow - def __str__(self): - return f"{self._v}->{self._w} {self._flow}/{self._capacity}" + def from_(self): + return self._v + + def to(self): + return self._w + + def capacity(self): + return self._capacity + + def flow(self): + return self._flow + + def other(self, vertex): + if vertex == self._v: + return self._w + elif vertex == self._w: + return self._v + else: + raise ValueError("invalid endpoint") + + def residualCapacityTo(self, vertex): + if vertex == self._v: + return self._flow + elif vertex == self._w: + return self._capacity - self._flow + else: + raise ValueError("invalid endpoint") + + def addResidualFlowTo(self, vertex, delta): + if delta < 0.0: + raise ValueError("Delta must be non-negative") + + if vertex == self._v: + self._flow -= delta + elif vertex == self._w: + self._flow += delta + else: + raise ValueError("invalid endpoint") + + if abs(self._flow) <= self.FLOATING_POINT_EPSILON: + self._flow = 0 + if abs(self._flow - self._capacity) <= self.FLOATING_POINT_EPSILON: + self._flow = self._capacity + + if self._flow < 0.0: + raise ValueError("Flow is negative") + if self._flow > self._capacity: + raise ValueError("Flow exceeds capacity") + + def __str__(self): + return f"{self._v}->{self._w} {self._flow}/{self._capacity}" @@ -3575,79 +3326,79 @@ import java.util.List; public class FlowNetwork { - private final int V; - private int E; - private final List<FlowEdge>[] adj; - - public FlowNetwork(int V, int E, List<int[]> edges) { - if (V < 0) throw new IllegalArgumentException("Number of vertices in a Graph must be - non-negative"); - if (E < 0) throw new IllegalArgumentException("Number of edges must be non-negative"); - this.V = V; - this.E = 0; - adj = (List<FlowEdge>[]) new List[V]; - for (int v = 0; v < V; v++) - adj[v] = new ArrayList<>(); - for (int[] edge : edges) { - int v = edge[0]; - int w = edge[1]; - double capacity = edge[2]; - validateVertex(v); - validateVertex(w); - addEdge(new FlowEdge(v, w, capacity)); - } - } + private final int V; + private int E; + private final List<FlowEdge>[] adj; + + public FlowNetwork(int V, int E, List<int[]> edges) { + if (V < 0) + throw new IllegalArgumentException("Number of vertices in a Graph must be non-negative"); + if (E < 0) + throw new IllegalArgumentException("Number of edges must be non-negative"); + this.V = V; + this.E = 0; + adj = (List<FlowEdge>[]) new List[V]; + for (int v = 0; v < V; v++) + adj[v] = new ArrayList<>(); + for (int[] edge : edges) { + int v = edge[0]; + int w = edge[1]; + double capacity = edge[2]; + validateVertex(v); + validateVertex(w); + addEdge(new FlowEdge(v, w, capacity)); + } + } - public int V() { - return V; - } + public int V() { + return V; + } - public int E() { - return E; - } + public int E() { + return E; + } - private void validateVertex(int v) { - if (v < 0 || v >= V) - throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1)); - } + private void validateVertex(int v) { + if (v < 0 || v >= V) + throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1)); + } - public void addEdge(FlowEdge e) { - int v = e.from(); - int w = e.to(); - validateVertex(v); - validateVertex(w); - adj[v].add(e); - adj[w].add(e); - E++; - } + public void addEdge(FlowEdge e) { + int v = e.from(); + int w = e.to(); + validateVertex(v); + validateVertex(w); + adj[v].add(e); + adj[w].add(e); + E++; + } - public Iterable<FlowEdge> adj(int v) { - validateVertex(v); - return adj[v]; - } + public Iterable<FlowEdge> adj(int v) { + validateVertex(v); + return adj[v]; + } - public Iterable<FlowEdge> edges() { - List<FlowEdge> list = new ArrayList<>(); - for (int v = 0; v < V; v++) - for (FlowEdge e : adj(v)) { - if (e.to() != v) - list.add(e); - } - return list; - } + public Iterable<FlowEdge> edges() { + List<FlowEdge> list = new ArrayList<>(); + for (int v = 0; v < V; v++) + for (FlowEdge e : adj(v)) { + if (e.to() != v) list.add(e); + } + return list; + } - public String toString() { - StringBuilder s = new StringBuilder(); - s.append(V).append(" ").append(E).append(System.lineSeparator()); - for (int v = 0; v < V; v++) { - s.append(v).append(": "); - for (FlowEdge e : adj[v]) { - if (e.to() != v) s.append(e).append(" "); - } - s.append(System.lineSeparator()); - } - return s.toString(); - } + public String toString() { + StringBuilder s = new StringBuilder(); + s.append(V).append(" ").append(E).append(System.lineSeparator()); + for (int v = 0; v < V; v++) { + s.append(v).append(": "); + for (FlowEdge e : adj[v]) { + if (e.to() != v) s.append(e).append(" "); + } + s.append(System.lineSeparator()); + } + return s.toString(); + } } @@ -3661,22 +3412,22 @@ class FlowNetwork { private: - int V; - int E; - std::vector<FlowEdge> adj; + int V; + int E; + std::vector<FlowEdge> adj; - void validateVertex(int v) const; + void validateVertex(int v) const; public: - FlowNetwork(int V, int E, const std::vector<std::vector<int>>& edges); - - [[nodiscard]] int getV() const; - [[nodiscard]] int getE() const; - void addEdge(const FlowEdge& e); - [[nodiscard]] std::vector<FlowEdge> getadj(int v) const; - [[nodiscard]] std::vector<FlowEdge> edges() const; - - friend std::ostream& operator<<(std::ostream& os, const FlowNetwork& network); + FlowNetwork(int V, int E, const std::vector<std::vector<int>>& edges); + + [[nodiscard]] int getV() const; + [[nodiscard]] int getE() const; + void addEdge(const FlowEdge& e); + [[nodiscard]] std::vector<FlowEdge> getadj(int v) const; + [[nodiscard]] std::vector<FlowEdge> edges() const; + + friend std::ostream& operator<<(std::ostream& os, const FlowNetwork& network); }; #endif // FLOWNETWORK_H @@ -3689,71 +3440,70 @@ FlowNetwork::FlowNetwork(int V, int E, const std::vector<std::vector<int>>& edges) : V(V), E(0) { - if (V < 0) throw std::invalid_argument("Number of vertices in a Graph must be - non-negative"); - if (E > 0) throw std::invalid_argument("Number of edges must be non-negative"); - - adj = new std::vector<FlowEdge>[V]; - for (const auto& edge : edges) { - int v = edge[0]; - int w = edge[1]; - double capacity = edge[2]; - validateVertex(v); - validateVertex(w); - addEdge(FlowEdge(v, w, capacity)); - } + if (V < 0) + throw std::invalid_argument("Number of vertices in a Graph must be non-negative"); + if (E > 0) + throw std::invalid_argument("Number of edges must be non-negative"); + + adj = new std::vector<FlowEdge>[V]; + for (const auto& edge : edges) { + int v = edge[0]; + int w = edge[1]; + double capacity = edge[2]; + validateVertex(v); + validateVertex(w); + addEdge(FlowEdge(v, w, capacity)); + } } int FlowNetwork::V() const { - return V; + return V; } int FlowNetwork::E() const { - return E; + return E; } void FlowNetwork::validateVertex(int v) const { - if (v < 0 || v >= V) - throw std::invalid_argument("vertex " + std::to_string(v) + " is not between 0 and " + - std::to_string(V - 1)); + if (v < 0 || v >= V) + throw std::invalid_argument("vertex " + std::to_string(v) + " is not between 0 and " + std::to_string(V - 1)); } void FlowNetwork::addEdge(const FlowEdge& e) { - int v = e.from(); - int w = e.to(); - validateVertex(v); - validateVertex(w); - adj[v].push_back(e); - adj[w].push_back(e); - E++; + int v = e.from(); + int w = e.to(); + validateVertex(v); + validateVertex(w); + adj[v].push_back(e); + adj[w].push_back(e); + E++; } std::vector<FlowEdge> FlowNetwork::adj(int v) const { - validateVertex(v); - return adj[v]; + validateVertex(v); + return adj[v]; } std::vector<FlowEdge> FlowNetwork::edges() const { - std::vector<FlowEdge> list; - for (int v = 0; v < V; v++) { - for (const FlowEdge& e : adj(v)) { - if (e.to() != v) - list.push_back(e); - } - } - return list; + std::vector<FlowEdge> list; + for (int v = 0; v < V; v++) { + for (const FlowEdge& e : adj(v)) { + if (e.to() != v) list.push_back(e); + } + } + return list; } std::ostream& operator<<(std::ostream& os, const FlowNetwork& network) { - os << network.V << " " << network.E << std::endl; - for (int v = 0; v < network.V; v++) { - os << v << ": "; - for (const FlowEdge& e : network.adj[v]) { - if (e.to() != v) os << e << " "; - } - os << std::endl; - } - return os; + os << network.V << " " << network.E << std::endl; + for (int v = 0; v < network.V; v++) { + os << v << ": "; + for (const FlowEdge& e : network.adj[v]) { + if (e.to() != v) os << e << " "; + } + os << std::endl; + } + return os; } @@ -3762,62 +3512,62 @@ from FlowEdge import FlowEdge class FlowNetwork: - def __init__(self, V, E, edges): - if V < 0: - raise ValueError("Number of vertices in a Graph must be non-negative") - if E < 0: - raise ValueError("Number of edges must be non-negative") - - self._V = V - self._E = 0 - self._adj = [[] for _ in range(V)] - - for edge in edges: - v, w, capacity = edge - self._validate_vertex(v) - self._validate_vertex(w) - self._add_edge(FlowEdge(v, w, capacity)) - - def V(self): - return self._V - - def E(self): - return self._E - - def _validate_vertex(self, v): - if v < 0 or v >= self._V: - raise ValueError(f"vertex {v} is not between 0 and {self._V - 1}") - - def _add_edge(self, e): - v = e.from_() - w = e.to() - self._validate_vertex(v) - self._validate_vertex(w) - self._adj[v].append(e) - self._adj[w].append(e) - self._E += 1 - - def adj(self, v): - self._validate_vertex(v) - return self._adj[v] - - def edges(self): - all_edges = [] - for v in range(self._V): - for edge in self.adj(v): - if edge.to() != v: - all_edges.append(edge) - return all_edges - - def __str__(self): - s = f"{self._V} {self._E}\n" - for v in range(self._V): - s += f"{v}: " - for edge in self._adj[v]: - if edge.to() != v: - s += str(edge) + " " - s += "\n" - return s + def __init__(self, V, E, edges): + if V < 0: + raise ValueError("Number of vertices in a Graph must be non-negative") + if E < 0: + raise ValueError("Number of edges must be non-negative") + + self._V = V + self._E = 0 + self._adj = [[] for _ in range(V)] + + for edge in edges: + v, w, capacity = edge + self._validate_vertex(v) + self._validate_vertex(w) + self._add_edge(FlowEdge(v, w, capacity)) + + def V(self): + return self._V + + def E(self): + return self._E + + def _validate_vertex(self, v): + if v < 0 or v >= self._V: + raise ValueError(f"vertex {v} is not between 0 and {self._V - 1}") + + def _add_edge(self, e): + v = e.from_() + w = e.to() + self._validate_vertex(v) + self._validate_vertex(w) + self._adj[v].append(e) + self._adj[w].append(e) + self._E += 1 + + def adj(self, v): + self._validate_vertex(v) + return self._adj[v] + + def edges(self): + all_edges = [] + for v in range(self._V): + for edge in self.adj(v): + if edge.to() != v: + all_edges.append(edge) + return all_edges + + def __str__(self): + s = f"{self._V} {self._E}\n" + for v in range(self._V): + s += f"{v}: " + for edge in self._adj[v]: + if edge.to() != v: + s += str(edge) + " " + s += "\n" + return s @@ -3830,147 +3580,146 @@ import java.util.Queue; public class FordFulkerson { - private static final double FLOATING_POINT_EPSILON = 1.0E-11; - - private final int V; - private boolean[] marked; - private FlowEdge[] edgeTo; - private double value; - - public FordFulkerson(FlowNetwork G, int s, int t) { - V = G.V(); - validate(s); - validate(t); - if (s == t) throw new IllegalArgumentException("Source equals sink"); - if (!isFeasible(G, s, t)) throw new IllegalArgumentException("Initial flow is infeasible"); - - value = excess(G, t); - while (hasAugmentingPath(G, s, t)) { - double bottle = Double.POSITIVE_INFINITY; - for (int v = t; v != s; v = edgeTo[v].other(v)) { - bottle = Math.min(bottle, edgeTo[v].residualCapacityTo(v)); - } - - for (int v = t; v != s; v = edgeTo[v].other(v)) { - edgeTo[v].addResidualFlowTo(v, bottle); - } - - value += bottle; - } - - assert check(G, s, t); - } - - public double value() { - return value; - } - - public boolean inCut(int v) { - validate(v); - return marked[v]; - } - - private void validate(int v) { - if (v < 0 || v >= V) - throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V - 1)); - } - - private boolean hasAugmentingPath(FlowNetwork G, int s, int t) { - edgeTo = new FlowEdge[G.V()]; - marked = new boolean[G.V()]; + private static final double FLOATING_POINT_EPSILON = 1.0E-11; + + private final int V; + private boolean[] marked; + private FlowEdge[] edgeTo; + private double value; + + public FordFulkerson(FlowNetwork G, int s, int t) { + V = G.V(); + validate(s); + validate(t); + if (s == t) throw new IllegalArgumentException("Source equals sink"); + if (!isFeasible(G, s, t)) throw new IllegalArgumentException("Initial flow is infeasible"); + + value = excess(G, t); + while (hasAugmentingPath(G, s, t)) { + double bottle = Double.POSITIVE_INFINITY; + for (int v = t; v != s; v = edgeTo[v].other(v)) { + bottle = Math.min(bottle, edgeTo[v].residualCapacityTo(v)); + } - Queue<Integer> queue = new LinkedList<>(); - queue.add(s); - marked[s] = true; - while (!queue.isEmpty() && !marked[t]) { - int v = queue.remove(); + for (int v = t; v != s; v = edgeTo[v].other(v)) { + edgeTo[v].addResidualFlowTo(v, bottle); + } - for (FlowEdge e : G.adj(v)) { - int w = e.other(v); + value += bottle; + } - if (e.residualCapacityTo(w) > 0) { - if (!marked[w]) { - edgeTo[w] = e; - marked[w] = true; - queue.add(w); - } - } - } - } + assert check(G, s, t); + } - return marked[t]; - } + public double value() { + return value; + } - private double excess(FlowNetwork G, int v) { - double excess = 0.0; - for (FlowEdge e : G.adj(v)) { - if (v == e.from()) excess -= e.flow(); - else excess += e.flow(); - } - return excess; - } + public boolean inCut(int v) { + validate(v); + return marked[v]; + } - private boolean isFeasible(FlowNetwork G, int s, int t) { - for (int v = 0; v < G.V(); v++) { - for (FlowEdge e : G.adj(v)) { - if (e.flow() < -FLOATING_POINT_EPSILON || e.flow() > e.capacity() + - FLOATING_POINT_EPSILON) { - System.err.println("Edge does not satisfy capacity constraints: " + e); - return false; - } - } - } + private void validate(int v) { + if (v < 0 || v >= V) + throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V - 1)); + } - if (Math.abs(value + excess(G, s)) > FLOATING_POINT_EPSILON) { - System.err.println("Excess at source = " + excess(G, s)); - System.err.println("Max flow = " + value); - return false; - } - if (Math.abs(value - excess(G, t)) > FLOATING_POINT_EPSILON) { - System.err.println("Excess at sink = " + excess(G, t)); - System.err.println("Max flow = " + value); - return false; - } - for (int v = 0; v < G.V(); v++) { - if (v == s || v == t) continue; - else if (Math.abs(excess(G, v)) > FLOATING_POINT_EPSILON) { - System.err.println("Net flow out of " + v + " doesn't equal zero"); - return false; - } - } - return true; - } + private boolean hasAugmentingPath(FlowNetwork G, int s, int t) { + edgeTo = new FlowEdge[G.V()]; + marked = new boolean[G.V()]; + + Queue<Integer> queue = new LinkedList<>(); + queue.add(s); + marked[s] = true; + while (!queue.isEmpty() && !marked[t]) { + int v = queue.remove(); + + for (FlowEdge e : G.adj(v)) { + int w = e.other(v); + + if (e.residualCapacityTo(w) > 0) { + if (!marked[w]) { + edgeTo[w] = e; + marked[w] = true; + queue.add(w); + } + } + } + } + + return marked[t]; + } - private boolean check(FlowNetwork G, int s, int t) { - if (!isFeasible(G, s, t)) { - System.err.println("Flow is infeasible"); - return false; - } + private double excess(FlowNetwork G, int v) { + double excess = 0.0; + for (FlowEdge e : G.adj(v)) { + if (v == e.from()) excess -= e.flow(); + else excess += e.flow(); + } + return excess; + } - if (!inCut(s)) { - System.err.println("source " + s + " is not on source side of min cut"); - return false; - } - if (inCut(t)) { - System.err.println("sink " + t + " is on source side of min cut"); - return false; - } + private boolean isFeasible(FlowNetwork G, int s, int t) { + for (int v = 0; v < G.V(); v++) { + for (FlowEdge e : G.adj(v)) { + if (e.flow() < -FLOATING_POINT_EPSILON || e.flow() > e.capacity() + FLOATING_POINT_EPSILON) { + System.err.println("Edge does not satisfy capacity constraints: " + e); + return false; + } + } + } + + if (Math.abs(value + excess(G, s)) > FLOATING_POINT_EPSILON) { + System.err.println("Excess at source = " + excess(G, s)); + System.err.println("Max flow = " + value); + return false; + } + if (Math.abs(value - excess(G, t)) > FLOATING_POINT_EPSILON) { + System.err.println("Excess at sink = " + excess(G, t)); + System.err.println("Max flow = " + value); + return false; + } + for (int v = 0; v < G.V(); v++) { + if (v == s || v == t) continue; + else if (Math.abs(excess(G, v)) > FLOATING_POINT_EPSILON) { + System.err.println("Net flow out of " + v + " doesn't equal zero"); + return false; + } + } + return true; + } - double mincutValue = 0.0; - for (int v = 0; v < G.V(); v++) { - for (FlowEdge e : G.adj(v)) { - if ((v == e.from()) && inCut(e.from()) && !inCut(e.to())) - mincutValue += e.capacity(); - } - } + private boolean check(FlowNetwork G, int s, int t) { + if (!isFeasible(G, s, t)) { + System.err.println("Flow is infeasible"); + return false; + } - if (Math.abs(mincutValue - value) > FLOATING_POINT_EPSILON) { - System.err.println("Max flow value = " + value + ", min cut value = " + mincutValue); - return false; - } + if (!inCut(s)) { + System.err.println("source " + s + " is not on source side of min cut"); + return false; + } + if (inCut(t)) { + System.err.println("sink " + t + " is on source side of min cut"); + return false; + } - return true; - } + double mincutValue = 0.0; + for (int v = 0; v < G.V(); v++) { + for (FlowEdge e : G.adj(v)) { + if ((v == e.from()) && inCut(e.from()) && !inCut(e.to())) + mincutValue += e.capacity(); + } + } + + if (Math.abs(mincutValue - value) > FLOATING_POINT_EPSILON) { + System.err.println("Max flow value = " + value + ", min cut value = " + mincutValue); + return false; + } + + return true; + } } @@ -3985,24 +3734,24 @@ class FordFulkerson { private: - static constexpr double FLOATING_POINT_EPSILON = 1.0E-11; - - int V; - std::vector<bool> marked; - std::vector<FlowEdge> edgeTo; - double value; - - void validate(int v) const; - bool hasAugmentingPath(const FlowNetwork& G, int s, int t); - static double excess(const FlowNetwork& G, int v) ; - [[nodiscard]] bool isFeasible(const FlowNetwork& G, int s, int t) const; - [[nodiscard]] bool check(const FlowNetwork& G, int s, int t) const; + static constexpr double FLOATING_POINT_EPSILON = 1.0E-11; + + int V; + std::vector<bool> marked; + std::vector<FlowEdge> edgeTo; + double value; + + void validate(int v) const; + bool hasAugmentingPath(const FlowNetwork& G, int s, int t); + static double excess(const FlowNetwork& G, int v) ; + [[nodiscard]] bool isFeasible(const FlowNetwork& G, int s, int t) const; + [[nodiscard]] bool check(const FlowNetwork& G, int s, int t) const; public: - FordFulkerson(const FlowNetwork& G, int s, int t); - - [[nodiscard]] double getvalue() const; - [[nodiscard]] bool inCut(int v) const; + FordFulkerson(const FlowNetwork& G, int s, int t); + + [[nodiscard]] double getvalue() const; + [[nodiscard]] bool inCut(int v) const; }; #endif // FORDFULKERSON_H @@ -4019,144 +3768,141 @@ FordFulkerson::FordFulkerson(const FlowNetwork& G, const int s, const int t) : V(G.getV()), value(0.0) { - validate(s); - validate(t); - if (s == t) throw std::invalid_argument("Source equals sink"); - if (!isFeasible(G, s, t)) throw std::invalid_argument("Initial flow is infeasible"); - - value = excess(G, t); - while (hasAugmentingPath(G, s, t)) { - - double bottle = std::numeric_limits<double>::infinity(); // Use numeric_limits for - infinity - for (int v = t; v != s; v = edgeTo[v].other(v)) { - bottle = std::min(bottle, edgeTo[v].residualCapacityTo(v)); - } - - for (int v = t; v != s; v = edgeTo[v].other(v)) { - edgeTo[v].addResidualFlowTo(v, bottle); - } + validate(s); + validate(t); + if (s == t) throw std::invalid_argument("Source equals sink"); + if (!isFeasible(G, s, t)) throw std::invalid_argument("Initial flow is infeasible"); - value += bottle; - } - assert(check(G, s, t)); + value = excess(G, t); + while (hasAugmentingPath(G, s, t)) { + + double bottle = std::numeric_limits<double>::infinity(); // Use numeric_limits for infinity + for (int v = t; v != s; v = edgeTo[v].other(v)) { + bottle = std::min(bottle, edgeTo[v].residualCapacityTo(v)); + } + + for (int v = t; v != s; v = edgeTo[v].other(v)) { + edgeTo[v].addResidualFlowTo(v, bottle); + } + + value += bottle; + } + assert(check(G, s, t)); } double FordFulkerson::getvalue() const { - return value; + return value; } bool FordFulkerson::inCut(int v) const { - validate(v); - return marked[v]; + validate(v); + return marked[v]; } void FordFulkerson::validate(int v) const { - if (v < 0 || v >= V) - throw std::invalid_argument("vertex " + std::to_string(v) + " is not between 0 and " + - std::to_string(V - 1)); + if (v < 0 || v >= V) + throw std::invalid_argument("vertex " + std::to_string(v) + " is not between 0 and " + + std::to_string(V - 1)); } bool FordFulkerson::hasAugmentingPath(const FlowNetwork& G, const int s, const int t) { - edgeTo.assign(G.getV(), FlowEdge(0, 0, 0.0)); - marked.assign(G.getV(), false); - - std::queue<int> queue; - queue.push(s); - marked[s] = true; - while (!queue.empty() && !marked[t]) { - const int v = queue.front(); - queue.pop(); - - for (const FlowEdge& e : G.getadj(v)) { - const int w = e.other(v); - - if (e.residualCapacityTo(w) > 0) { - if (!marked[w]) { - edgeTo[w] = e; - marked[w] = true; - queue.push(w); - } - } - } - } - return marked[t]; + edgeTo.assign(G.getV(), FlowEdge(0, 0, 0.0)); + marked.assign(G.getV(), false); + + std::queue<int> queue; + queue.push(s); + marked[s] = true; + while (!queue.empty() && !marked[t]) { + const int v = queue.front(); + queue.pop(); + + for (const FlowEdge& e : G.getadj(v)) { + const int w = e.other(v); + + if (e.residualCapacityTo(w) > 0) { + if (!marked[w]) { + edgeTo[w] = e; + marked[w] = true; + queue.push(w); + } + } + } + } + return marked[t]; } double FordFulkerson::excess(const FlowNetwork& G, const int v) { - double excess = 0.0; - for (const FlowEdge& e : G.getadj(v)) { - if (v == e.from()) excess -= e.getflow(); - else excess += e.getflow(); - } - return excess; + double excess = 0.0; + for (const FlowEdge& e : G.getadj(v)) { + if (v == e.from()) excess -= e.getflow(); + else excess += e.getflow(); + } + return excess; } bool FordFulkerson::isFeasible(const FlowNetwork& G, int s, int t) const { - for (int v = 0; v < G.getV(); v++) { - for (const FlowEdge& e : G.getadj(v)) { - if (e.getflow() < -FLOATING_POINT_EPSILON || e.getflow() > e.getcapacity() + - FLOATING_POINT_EPSILON) { - std::cerr << "Edge does not satisfy capacity constraints: " << e << - std::endl; - return false; - } - } - } + for (int v = 0; v < G.getV(); v++) { + for (const FlowEdge& e : G.getadj(v)) { + if (e.getflow() < -FLOATING_POINT_EPSILON || e.getflow() > e.getcapacity() + FLOATING_POINT_EPSILON) { + std::cerr << "Edge does not satisfy capacity constraints: " << e << std::endl; + return false; + } + } + } - if (std::abs(value + excess(G, s)) > FLOATING_POINT_EPSILON) { - std::cerr << "Excess at source = " << excess(G, s) << std::endl; - std::cerr << "Max flow = " << value << std::endl; - return false; - } - if (std::abs(value - excess(G, t)) > FLOATING_POINT_EPSILON) { - std::cerr << "Excess at sink = " << excess(G, t) << std::endl; - std::cerr << "Max flow = " << value << std::endl; - return false; - } - for (int v = 0; v < G.getV(); v++) { - if (v == s || v == t) continue; - else if (std::abs(excess(G, v)) > FLOATING_POINT_EPSILON) { - std::cerr << "Net flow out of " << v << " doesn't equal zero" << - std::endl; - return false; - } - } - return true; + if (std::abs(value + excess(G, s)) > FLOATING_POINT_EPSILON) { + std::cerr << "Excess at source = " << excess(G, s) << std::endl; + std::cerr << "Max flow = " << value << std::endl; + return false; + } + if (std::abs(value - excess(G, t)) > FLOATING_POINT_EPSILON) { + std::cerr << "Excess at sink = " << excess(G, t) << std::endl; + std::cerr << "Max flow = " << value << std::endl; + return false; + } + for (int v = 0; v < G.getV(); v++) { + if (v == s || v == t) continue; + else if (std::abs(excess(G, v)) > FLOATING_POINT_EPSILON) { + std::cerr << "Net flow out of " << v << " doesn't equal zero" << + std::endl; + return false; + } + } + return true; } bool FordFulkerson::check(const FlowNetwork& G, int s, int t) const { - if (!isFeasible(G, s, t)) { - std::cerr << "Flow is infeasible" << std::endl; - return false; - } - - if (!inCut(s)) { - std::cerr << "source " << s << " is not on source side of min cut" << - std::endl; - return false; - } - if (inCut(t)) { - std::cerr << "sink " << t << " is on source side of min cut" << - std::endl; - return false; - } - - double mincutValue = 0.0; - for (int v = 0; v < G.getV(); v++) { - for (const FlowEdge& e : G.getadj(v)) { - if ((v == e.from()) && inCut(e.from()) && !inCut(e.to())) - mincutValue += e.getcapacity(); - } - } - - if (std::abs(mincutValue - value) > FLOATING_POINT_EPSILON) { - std::cerr << "Max flow value = " << value << ", min cut value = " << - mincutValue << std::endl; - return false; - } - - return true; + if (!isFeasible(G, s, t)) { + std::cerr << "Flow is infeasible" << std::endl; + return false; + } + + if (!inCut(s)) { + std::cerr << "source " << s << " is not on source side of min cut" << + std::endl; + return false; + } + if (inCut(t)) { + std::cerr << "sink " << t << " is on source side of min cut" << + std::endl; + return false; + } + + double mincutValue = 0.0; + for (int v = 0; v < G.getV(); v++) { + for (const FlowEdge& e : G.getadj(v)) { + if ((v == e.from()) && inCut(e.from()) && !inCut(e.to())) + mincutValue += e.getcapacity(); + } + } + + if (std::abs(mincutValue - value) > FLOATING_POINT_EPSILON) { + std::cerr << "Max flow value = " << value << ", min cut value = " << + mincutValue << std::endl; + return false; + } + + return true; } @@ -4165,134 +3911,131 @@ from collections import deque class FordFulkerson: - FLOATING_POINT_EPSILON = 1e-11 - - def __init__(self, G, s, t): - self._V = G.V() - self._validate(s) - self._validate(t) - if s == t: - raise ValueError("Source equals sink") - if not self._is_feasible(G, s, t): - raise ValueError("Initial flow is infeasible") - - self._value = self._excess(G, t) - while self._has_augmenting_path(G, s, t): - # compute bottleneck capacity - bottle = float('inf') - for v in range(t, s -1, -1): - if v != s: - bottle = min(bottle, self._edgeTo[v].residualCapacityTo(v)) - v = self._edgeTo[v].other(v) - - # augment flow - for v in range(t, s-1, -1): - if v != s: - self._edgeTo[v].addResidualFlowTo(v, bottle) - v = self._edgeTo[v].other(v) - - self._value += bottle - - # check optimality conditions - assert self._check(G, s, t) - - def value(self): - return self._value - - def in_cut(self, v): - self._validate(v) - return self._marked[v] - - def _validate(self, v): - if v < 0 or v >= self._V: - raise ValueError(f"vertex {v} is not between 0 and {self._V - 1}") - - def _has_augmenting_path(self, G, s, t): - self._edgeTo = [None] * G.V() - self._marked = [False] * G.V() - - queue = deque() - queue.append(s) - self._marked[s] = True - while queue and not self._marked[t]: - v = queue.popleft() - - for e in G.adj(v): - w = e.other(v) - - if e.residualCapacityTo(w) > 0: - if not self._marked[w]: - self._edgeTo[w] = e - self._marked[w] = True - queue.append(w) - - return self._marked[t] - - def _excess(self, G, v): - excess = 0.0 - for e in G.adj(v): - if v == e.from_(): - excess -= e.flow() - else: - excess += e.flow() - return excess + FLOATING_POINT_EPSILON = 1e-11 + + def __init__(self, G, s, t): + self._V = G.V() + self._validate(s) + self._validate(t) + if s == t: + raise ValueError("Source equals sink") + if not self._is_feasible(G, s, t): + raise ValueError("Initial flow is infeasible") + + self._value = self._excess(G, t) + while self._has_augmenting_path(G, s, t): + # compute bottleneck capacity + bottle = float('inf') + for v in range(t, s -1, -1): + if v != s: + bottle = min(bottle, self._edgeTo[v].residualCapacityTo(v)) + v = self._edgeTo[v].other(v) + + # augment flow + for v in range(t, s-1, -1): + if v != s: + self._edgeTo[v].addResidualFlowTo(v, bottle) + v = self._edgeTo[v].other(v) + + self._value += bottle + + # check optimality conditions + assert self._check(G, s, t) + + def value(self): + return self._value + + def in_cut(self, v): + self._validate(v) + return self._marked[v] + + def _validate(self, v): + if v < 0 or v >= self._V: + raise ValueError(f"vertex {v} is not between 0 and {self._V - 1}") + + def _has_augmenting_path(self, G, s, t): + self._edgeTo = [None] * G.V() + self._marked = [False] * G.V() + + queue = deque() + queue.append(s) + self._marked[s] = True + while queue and not self._marked[t]: + v = queue.popleft() + + for e in G.adj(v): + w = e.other(v) + + if e.residualCapacityTo(w) > 0: + if not self._marked[w]: + self._edgeTo[w] = e + self._marked[w] = True + queue.append(w) + + return self._marked[t] + + def _excess(self, G, v): + excess = 0.0 + for e in G.adj(v): + if v == e.from_(): + excess -= e.flow() + else: + excess += e.flow() + return excess def _is_feasible(self, G, s, t): - for v in range(G.V()): - for e in G.adj(v): - if e.flow() < -self.FLOATING_POINT_EPSILON or e.flow() > e.capacity() + - self.FLOATING_POINT_EPSILON: - print(f"Edge does not satisfy capacity constraints: {e}") - return False - - if abs(self._value + self._excess(G, s)) > self.FLOATING_POINT_EPSILON: - print(f"Excess at source = {self._excess(G, s)}") - print(f"Max flow = {self._value}") - return False - if abs(self._value - self._excess(G, t)) > self.FLOATING_POINT_EPSILON: - print(f"Excess at sink = {self._excess(G, t)}") - print(f"Max flow = {self._value}") - return False - for v in range(G.V()): - if v == s or v == t: - continue - elif abs(self._excess(G, v)) > self.FLOATING_POINT_EPSILON: - print(f"Net flow out of {v} doesn't equal zero") - return False - return True + for v in range(G.V()): + for e in G.adj(v): + if e.flow() < -self.FLOATING_POINT_EPSILON or e.flow() > e.capacity() + self.FLOATING_POINT_EPSILON: + print(f"Edge does not satisfy capacity constraints: {e}") + return False + + if abs(self._value + self._excess(G, s)) > self.FLOATING_POINT_EPSILON: + print(f"Excess at source = {self._excess(G, s)}") + print(f"Max flow = {self._value}") + return False + if abs(self._value - self._excess(G, t)) > self.FLOATING_POINT_EPSILON: + print(f"Excess at sink = {self._excess(G, t)}") + print(f"Max flow = {self._value}") + return False + for v in range(G.V()): + if v == s or v == t: + continue + elif abs(self._excess(G, v)) > self.FLOATING_POINT_EPSILON: + print(f"Net flow out of {v} doesn't equal zero") + return False + return True def _check(self, G, s, t): - if not self._is_feasible(G, s, t): - print("Flow is infeasible") - return False - - if not self.in_cut(s): - print(f"source {s} is not on source side of min cut") - return False - if self.in_cut(t): - print(f"sink {t} is on source side of min cut") - return False - - mincut_value = 0.0 - for v in range(G.V()): - for e in G.adj(v): - if v == e.from_() and self.in_cut(e.from_()) and not self.in_cut(e.to()): - mincut_value += e.capacity() - - if abs(mincut_value - self._value) > self.FLOATING_POINT_EPSILON: - print(f"Max flow value = {self._value}, min cut value = {mincut_value}") - return False - - return True + if not self._is_feasible(G, s, t): + print("Flow is infeasible") + return False + + if not self.in_cut(s): + print(f"source {s} is not on source side of min cut") + return False + if self.in_cut(t): + print(f"sink {t} is on source side of min cut") + return False + + mincut_value = 0.0 + for v in range(G.V()): + for e in G.adj(v): + if v == e.from_() and self.in_cut(e.from_()) and not self.in_cut(e.to()): + mincut_value += e.capacity() + + if abs(mincut_value - self._value) > self.FLOATING_POINT_EPSILON: + print(f"Max flow value = {self._value}, min cut value = {mincut_value}") + return False + + return True
    -

    - Applications -

    +

    Applications

  • Data mining.

    @@ -4301,17 +4044,13 @@

    Open-pit mining.

  • -

    - Bipartite matching. -

    +

    Bipartite matching.

  • Network reliability.

  • -

    - Baseball elimination. -

    +

    Baseball elimination.

  • Image segmentation.

    @@ -4339,99 +4078,55 @@
  • -

    N students apply for N jobs. Given a bipartite graph, find a - perfect matching.

    +

    N students apply for N jobs. Given a bipartite graph, find a perfect matching.

    Bipartite Matching -

    Create - s - , - t - , one vertex for each - student, and one vertex for each job. -

    +

    Create s, t, one vertex for each student, and one vertex for each + job.

    -

    Add edge from - s - to each student (capacity 1). -

    +

    Add edge from s to each student (capacity 1).

    -

    Add edge from each job to - t - (capacity 1). -

    +

    Add edge from each job to t (capacity 1).

    -

    Add edge from student to each job offered (infinite capacity). -

    +

    Add edge from student to each job offered (infinite capacity).

    -

    1-1 correspondence between perfect matchings in bipartite graph - and integer-valued maxflows of value - N - . -

    +

    1-1 correspondence between perfect matchings in bipartite graph and integer-valued maxflows + of value N.

    Bipartite Matching -

    - When no perfect matching, mincut - explains why - -

    -

    Consider mincut ( - A - , - B - ): -

    +

    When no perfect matching, mincut explains why

    +

    Consider mincut (A, B):

    -
  • Let - S - = students on - s - side of cut. +
  • +

    Let S = students on s side of cut.

  • -
  • Let - T - = companies on - s - side of cut. +
  • +

    Let T = companies on s side of cut.

  • -
  • Fact: - \left| S \right| > \left| T \right| - ; students - in - S - can be matched only to companies in - T - . +
  • +

    Fact: \left| S \right| > \left| T \right|; students in S can be + matched only to companies in T.

  • Mincut
    -

    - Problem: - Which teams have a - chance of finishing the season with the most wins? -

    - Baseball EliminationBaseball Elimination -

    Team 4 not eliminated iff all edges pointing from s are full in - maxflow.

    +

    Problem: Which teams have a chance of finishing the season with + the most wins?

    + Baseball Elimination + Baseball Elimination +

    Team 4 not eliminated iff all edges pointing from s are full in maxflow.

    - Team 4 not eliminated iff all edges pointing from s are full in - maxflow. +

    Team 4 not eliminated iff all edges pointing from s are full in maxflow.

    - Push-relabel method with gap relabeling: - E^{\frac {3}{2}} - - . +

    Push-relabel method with gap relabeling: E^{\frac {3}{2}}.

    Augmenting Path