From 20dc3ca88e4639350f3c88ae67a87c2a21f4a368 Mon Sep 17 00:00:00 2001 From: Igor Date: Tue, 9 Jul 2024 17:55:13 -0300 Subject: [PATCH 1/3] feat: add gomory-hu implementation and description --- src/graph/gomory_hu.md | 88 ++++++++++++++++++++++++++++++++++++++++++ src/navigation.md | 1 + 2 files changed, 89 insertions(+) create mode 100644 src/graph/gomory_hu.md diff --git a/src/graph/gomory_hu.md b/src/graph/gomory_hu.md new file mode 100644 index 000000000..2e3567927 --- /dev/null +++ b/src/graph/gomory_hu.md @@ -0,0 +1,88 @@ +# Gomory Hu Tree + +## Definition + +The gomory-hu tree of an undirected graph with capacities consists of a weighted tree that condenses information from all the *s-t cuts* for all s-t vertex pairs in the graph. Naively, one must think that $O(|V|^2)$ flow computations are needed to build this data structure, but actually it can be shown that only $|V| - 1$ flow computations are needed. Once the tree is constructed, we can get the minimum cut between two vertices *s* and *t* by querying the minimum weight edge in the unique *s-t* path. + +## Gusfield's Simplification Algorithm + +We can say that two cuts (X, Y) and (U, V) *cross* if all four set intersections $X \cap U$, $X \cap V$, $Y \cap U$, $Y \cap V$ are nonempty. Most of the work of the original gomory-hu method is involved in maintaining the noncrossing condition. The following simpler, yet efficient method, proposed by Gusfield uses crossing cuts to produce equivalent flow trees. + +## Complexity + +The algorithm total complexity is $\mathcal{O}(V*MaxFlow)$, wich means that the overall complexity depends on the algorithm that was choosen to find the maximum flow. + +### Implementation +This implementation considers the Gomory-Hu tree as a struct with methods: + +- The maximum flow algorithm must also be a struct with methods, in the implementation bellow we utilize Dinic's algorithm to calculate the maximum flow. + +- The algorithm is 0-indexed and will root the tree in node 0. + +- The method *solve* returns the list of edges of the Gomory-Hu tree. + +```{.cpp file=gomoryhu} +struct gomory_hu { + struct edg{ + int u, v, cap; + }; + + Dinic dinic; // you can change your Max Flow algorithm here + // !! if you change remember to make it compatible with the rest of the code !! + + vector edgs; + + void add_edge(int u, int v, int cap) { // the edges are already bidirectional + edgs.push_back({u, v, cap}); + } + + vector vis; + + void dfs(int a) { + if (vis[a]) return; + vis[a] = 1; + for (auto &e : dinic.adj[a]) + if (e.c - e.flow() > 0) + dfs(e.to); + } + + vector> solve(int n) { + vector> tree_edges(n); // if i > 0, stores pair(cost, parent). + + for (int i = 1; i < n; i++) { + dinic = Dinic(n); + + for (auto &e : edgs) dinic.addEdge(e.u, e.v, e.cap); + tree_edges[i].first = dinic.calc(i, tree_edges[i].second); + + vis.assign(n, 0); + dfs(i); + + for (int j = i + 1; j < n; j++) { + if (tree_edges[j].second == tree_edges[i].second && vis[j]) + tree_edges[j].second = i; + } + } + + return tree_edges; + } +}; +``` + +## Task examples + +Here are some examples of problems related to the Gomory-Hu tree: + +- Given a weighted and connected graph, find the minimun s-t cut for all pair of vertices. + +- Given a weighted and connected graph, find the minimum/maximum s-t cut among all pair of vertices. + +- Find an approximate solution for the [Minimum K-Cut problem](https://en.wikipedia.org/wiki/Minimum_k-cut). + +## Practice Problems + +- [Codeforces - Juice Junctions](https://codeforces.com/gym/101480/attachments) + +- [Codeforces - Honeycomb](https://codeforces.com/gym/103652/problem/D) + +- [Codeforces - Pumping Stations](https://codeforces.com/contest/343/problem/E) diff --git a/src/navigation.md b/src/navigation.md index 05c662e50..8f70c2720 100644 --- a/src/navigation.md +++ b/src/navigation.md @@ -189,6 +189,7 @@ search: - [Flows with demands](graph/flow_with_demands.md) - [Minimum-cost flow](graph/min_cost_flow.md) - [Assignment problem](graph/Assignment-problem-min-flow.md) + - [All-pairs minimum cut - Gomory Hu](graph/gomory_hu.md) - Matchings and related problems - [Bipartite Graph Check](graph/bipartite-check.md) - [Kuhn's Algorithm - Maximum Bipartite Matching](graph/kuhn_maximum_bipartite_matching.md) From a40adf7b9badbdfc5a2bd7770dc018d27cbf8728 Mon Sep 17 00:00:00 2001 From: Igor Date: Tue, 9 Jul 2024 18:29:06 -0300 Subject: [PATCH 2/3] fix(gomory-hu): typos and improve style --- src/graph/gomory_hu.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/graph/gomory_hu.md b/src/graph/gomory_hu.md index 2e3567927..d983efe72 100644 --- a/src/graph/gomory_hu.md +++ b/src/graph/gomory_hu.md @@ -6,7 +6,7 @@ The gomory-hu tree of an undirected graph with capacities consists of a weighted ## Gusfield's Simplification Algorithm -We can say that two cuts (X, Y) and (U, V) *cross* if all four set intersections $X \cap U$, $X \cap V$, $Y \cap U$, $Y \cap V$ are nonempty. Most of the work of the original gomory-hu method is involved in maintaining the noncrossing condition. The following simpler, yet efficient method, proposed by Gusfield uses crossing cuts to produce equivalent flow trees. +We can say that two cuts $(X, Y)$ and $(U, V)$ *cross* if all four set intersections $X \cap U$, $X \cap V$, $Y \cap U$, $Y \cap V$ are nonempty. Most of the work of the original gomory-hu method is involved in maintaining the noncrossing condition. The following simpler, yet efficient method, proposed by Gusfield uses crossing cuts to produce equivalent flow trees. ## Complexity @@ -15,11 +15,11 @@ The algorithm total complexity is $\mathcal{O}(V*MaxFlow)$, wich means that the ### Implementation This implementation considers the Gomory-Hu tree as a struct with methods: -- The maximum flow algorithm must also be a struct with methods, in the implementation bellow we utilize Dinic's algorithm to calculate the maximum flow. +- The maximum flow algorithm must also be a struct with methods, in the implementation below we utilize Dinic's algorithm to calculate the maximum flow. - The algorithm is 0-indexed and will root the tree in node 0. -- The method *solve* returns the list of edges of the Gomory-Hu tree. +- The method *solve* returns a list that contains for each index $i$ the cost of the edge connecting $i$ and its parent, and the parent number. ```{.cpp file=gomoryhu} struct gomory_hu { From c278efac361dde9901fb56105f5cac703417a777 Mon Sep 17 00:00:00 2001 From: Igor Date: Tue, 9 Jul 2024 19:09:11 -0300 Subject: [PATCH 3/3] feat(gomory-hu): add step-by-step description of the algorithm --- src/graph/gomory_hu.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/graph/gomory_hu.md b/src/graph/gomory_hu.md index d983efe72..5ea241016 100644 --- a/src/graph/gomory_hu.md +++ b/src/graph/gomory_hu.md @@ -2,12 +2,22 @@ ## Definition -The gomory-hu tree of an undirected graph with capacities consists of a weighted tree that condenses information from all the *s-t cuts* for all s-t vertex pairs in the graph. Naively, one must think that $O(|V|^2)$ flow computations are needed to build this data structure, but actually it can be shown that only $|V| - 1$ flow computations are needed. Once the tree is constructed, we can get the minimum cut between two vertices *s* and *t* by querying the minimum weight edge in the unique *s-t* path. +The gomory-hu tree of an undirected graph $G$ with capacities consists of a weighted tree that condenses information from all the *s-t cuts* for all s-t vertex pairs in the graph. Naively, one must think that $O(|V|^2)$ flow computations are needed to build this data structure, but actually it can be shown that only $|V| - 1$ flow computations are needed. Once the tree is constructed, we can get the minimum cut between two vertices *s* and *t* by querying the minimum weight edge in the unique *s-t* path. ## Gusfield's Simplification Algorithm We can say that two cuts $(X, Y)$ and $(U, V)$ *cross* if all four set intersections $X \cap U$, $X \cap V$, $Y \cap U$, $Y \cap V$ are nonempty. Most of the work of the original gomory-hu method is involved in maintaining the noncrossing condition. The following simpler, yet efficient method, proposed by Gusfield uses crossing cuts to produce equivalent flow trees. +Lets assume the vertices are 0-indexed for the next section +The algorithm is composed of the following steps: + +1. Create a (star) tree $T'$ on $n$ nodes, with node 0 at the center and nodes 1 through $n - 1$ at the leaves. +2. For $i$ from 1 to $n - 1$ do steps 3 and 4 +3. Compute the minimum cut $(X, Y)$ in $G$ between (leaf) node $i$ and its (unique) neighbor $t$ in $T'$. Label the edge $(i, t)$ in $T'$ with the capacity of the $(X, Y)$ cut. +4. For every node $j$ larger than $i$, if $j$ is a neighbor of $t$ and $j$ is on the $i$ side of $(X, Y)$, then modify $T'$ by disconnecting $j$ from $t$ and connecting $j$ to $i$. Note that each node $j$ larger than $i$ remains a leaf in $T'$ + +It is easy to see that at every iteration, node $i$ and all nodes larger than $i$ are leaves in $T'$, as required by the algorithm. + ## Complexity The algorithm total complexity is $\mathcal{O}(V*MaxFlow)$, wich means that the overall complexity depends on the algorithm that was choosen to find the maximum flow. @@ -21,6 +31,8 @@ This implementation considers the Gomory-Hu tree as a struct with methods: - The method *solve* returns a list that contains for each index $i$ the cost of the edge connecting $i$ and its parent, and the parent number. +- Note that the algorithm doesn't produce a *cut tree*, only an *equivalent flow tree*, so one cannot retrieve the two components of a cut from the tree $T'$. + ```{.cpp file=gomoryhu} struct gomory_hu { struct edg{