diff --git a/content/graphs/Dinic.h b/content/graphs/Dinic.h new file mode 100644 index 00000000..6302bff0 --- /dev/null +++ b/content/graphs/Dinic.h @@ -0,0 +1,90 @@ +/** + * Description: Max flow algorithm. Can find a valid + * circulation given vertex and/or edge demands. Time: + * $O(VE\log{U})$ + */ + +struct Dinic { + // disable scaling when max flow/capacity is small, or + // sometimes on random data + const static bool SCALING = true; + struct Edge { + int v, dual; + ll cap, res; + constexpr ll flow() { return max(cap - res, 0ll); } + }; + int n, s, t; + vector lvl, q, blk; + vector> adj; + vector> edges; + Dinic(int n): n(n + 2), s(n++), t(n++), q(n), adj(n) {} + int add(int u, int v, ll cap, ll flow = 0) { + adj[u].push_back({v, int(adj[v].size()), cap, cap - flow}); + adj[v].push_back({u, int(adj[u].size()) - 1, 0, 0}); + edges.emplace_back(u, adj[u].size() - 1); + return edges.size() - 1; // this Edge's ID + } + ll dfs(int u, ll in) { + if (u == t || !in) return in; + ll flow = 0; + for (auto& e : adj[u]) { + if (e.res && !blk[e.v] && lvl[e.v] == lvl[u] - 1) + if (ll out = dfs(e.v, min(in, e.res))) { + flow += out, in -= out, e.res -= out; + adj[e.v][e.dual].res += out; + if (!in) return flow; + } + } + blk[u] = 1; + return flow; + } + ll flow() { + ll flow = 0; + q[0] = t; + for (int B = SCALING * 30; B >= 0; B--) do { + lvl = blk = vector(n); + int qi = 0, qe = lvl[t] = 1; + while (qi < qe && !lvl[s]) { + int u = q[qi++]; + for (auto& e : adj[u]) + if (!lvl[e.v] && adj[e.v][e.dual].res >> B) + q[qe++] = e.v, lvl[e.v] = lvl[u] + 1; + } + if (lvl[s]) flow += dfs(s, LLONG_MAX); + } while (lvl[s]); + return flow; + } + Edge& get(int id) { // get Edge object from its ID + return adj[edges[id].first][edges[id].second]; + } + void clear() { + for (auto& it : adj) + for (auto& e : it) e.res = e.cap; + } + bool leftOfMinCut(int u) { return lvl[u] == 0; } + // d is a list of vertex demands, d[u] = flow in - flow out + // negative if u is a source, positive if u is a sink + bool circulation(vector d = {}) { + d.resize(n); + vector circEdges; + Dinic g(n); + for (int u = 0; u < n; u++) + for (auto& e : adj[u]) { + d[u] += e.flow(), d[e.v] -= e.flow(); + if (e.res) circEdges.push_back(g.add(u, e.v, e.res)); + } + int tylerEdge = g.add(t, s, LLONG_MAX, 0); + ll flow = 0; + for (int u = 0; u < n; u++) + if (d[u] < 0) + g.add(g.s, u, -d[u]); + else if (d[u] > 0) + g.add(u, g.t, d[u]), flow += d[u]; + if (flow != g.flow()) return false; + int i = 0; // reconstruct the flow into this graph + for (int u = 0; u < n; u++) + for (auto& e : adj[u]) + if (e.res) e.res -= g.get(circEdges[i++]).flow(); + return true; + } +}; diff --git a/content/graphs/chapter.tex b/content/graphs/chapter.tex index fb55753d..0cdc5910 100644 --- a/content/graphs/chapter.tex +++ b/content/graphs/chapter.tex @@ -3,3 +3,4 @@ \chapter{Graphs} % \kactlimport{Dijkstra.h} % probably shouldn't be printed \kactlimport{SCCTarjan.h} \kactlimport{SCCKosaraju.h} +\kactlimport{Dinic.h} diff --git a/kactl.pdf b/kactl.pdf index bec569aa..d813dd40 100644 Binary files a/kactl.pdf and b/kactl.pdf differ diff --git a/snippets/cpp.json b/snippets/cpp.json index b2f5cc17..d23f7cf2 100644 --- a/snippets/cpp.json +++ b/snippets/cpp.json @@ -80,6 +80,98 @@ "hack_Dijkstra" ] }, + "Dinic.h": { + "body": [ + "struct Dinic {", + "\t// disable scaling when max flow/capacity is small, or", + "\t// sometimes on random data", + "\tconst static bool SCALING = true;", + "\tstruct Edge {", + "\t\tint v, dual;", + "\t\tll cap, res;", + "\t\tconstexpr ll flow() { return max(cap - res, 0ll); }", + "\t};", + "\tint n, s, t;", + "\tvector lvl, q, blk;", + "\tvector> adj;", + "\tvector> edges;", + "\tDinic(int n) : n(n + 2), s(n++), t(n++), q(n), adj(n) {}", + "\tint add(int u, int v, ll cap, ll flow = 0) {", + "\t\tadj[u].push_back({v, int(adj[v].size()), cap, cap - flow});", + "\t\tadj[v].push_back({u, int(adj[u].size()) - 1, 0, 0});", + "\t\tedges.emplace_back(u, adj[u].size() - 1);", + "\t\treturn edges.size() - 1; // this Edge's ID", + "\t}", + "\tll dfs(int u, ll in) {", + "\t\tif (u == t || !in) return in;", + "\t\tll flow = 0;", + "\t\tfor (auto& e : adj[u]) {", + "\t\t\tif (e.res && !blk[e.v] && lvl[e.v] == lvl[u] - 1)", + "\t\t\t\tif (ll out = dfs(e.v, min(in, e.res))) {", + "\t\t\t\t\tflow += out, in -= out, e.res -= out;", + "\t\t\t\t\tadj[e.v][e.dual].res += out;", + "\t\t\t\t\tif (!in) return flow;", + "\t\t\t\t}", + "\t\t}", + "\t\tblk[u] = 1;", + "\t\treturn flow;", + "\t}", + "\tll flow() {", + "\t\tll flow = 0;", + "\t\tq[0] = t;", + "\t\tfor (int B = SCALING * 30; B >= 0; B--) do {", + "\t\t\t\tlvl = blk = vector(n);", + "\t\t\t\tint qi = 0, qe = lvl[t] = 1;", + "\t\t\t\twhile (qi < qe && !lvl[s]) {", + "\t\t\t\t\tint u = q[qi++];", + "\t\t\t\t\tfor (auto& e : adj[u])", + "\t\t\t\t\t\tif (!lvl[e.v] && adj[e.v][e.dual].res >> B)", + "\t\t\t\t\t\t\tq[qe++] = e.v, lvl[e.v] = lvl[u] + 1;", + "\t\t\t\t}", + "\t\t\t\tif (lvl[s]) flow += dfs(s, LLONG_MAX);", + "\t\t\t} while (lvl[s]);", + "\t\treturn flow;", + "\t}", + "\tEdge& get(int id) { // get Edge object from its ID", + "\t\treturn adj[edges[id].first][edges[id].second];", + "\t}", + "\tvoid clear() {", + "\t\tfor (auto& it : adj)", + "\t\t\tfor (auto& e : it) e.res = e.cap;", + "\t}", + "\tbool leftOfMinCut(int u) { return lvl[u] == 0; }", + "\t// d is a list of vertex demands, d[u] = flow in - flow out", + "\t// negative if u is a source, positive if u is a sink", + "\tbool circulation(vector d = {}) {", + "\t\td.resize(n);", + "\t\tvector circEdges;", + "\t\tDinic g(n);", + "\t\tfor (int u = 0; u < n; u++)", + "\t\t\tfor (auto& e : adj[u]) {", + "\t\t\t\td[u] += e.flow(), d[e.v] -= e.flow();", + "\t\t\t\tif (e.res) circEdges.push_back(g.add(u, e.v, e.res));", + "\t\t\t}", + "\t\tint tylerEdge = g.add(t, s, LLONG_MAX, 0);", + "\t\tll flow = 0;", + "\t\tfor (int u = 0; u < n; u++)", + "\t\t\tif (d[u] < 0)", + "\t\t\t\tg.add(g.s, u, -d[u]);", + "\t\t\telse if (d[u] > 0)", + "\t\t\t\tg.add(u, g.t, d[u]), flow += d[u];", + "\t\tif (flow != g.flow()) return false;", + "\t\tint i = 0; // reconstruct the flow into this graph", + "\t\tfor (int u = 0; u < n; u++)", + "\t\t\tfor (auto& e : adj[u])", + "\t\t\t\tif (e.res) e.res -= g.get(circEdges[i++]).flow();", + "\t\treturn true;", + "\t}", + "};" + ], + "description": "Max flow algorithm. Can find a valid\ncirculation given vertex and/or edge demands. Time:\n$O(VE\\log{U})$", + "prefix": [ + "hack_Dinic" + ] + }, "Fraction.h": { "body": [ "template ", diff --git a/snippets/cpp/Dinic.json b/snippets/cpp/Dinic.json new file mode 100644 index 00000000..9a28bfb1 --- /dev/null +++ b/snippets/cpp/Dinic.json @@ -0,0 +1,94 @@ +{ + "Dinic.h": { + "body": [ + "struct Dinic {", + "\t// disable scaling when max flow/capacity is small, or", + "\t// sometimes on random data", + "\tconst static bool SCALING = true;", + "\tstruct Edge {", + "\t\tint v, dual;", + "\t\tll cap, res;", + "\t\tconstexpr ll flow() { return max(cap - res, 0ll); }", + "\t};", + "\tint n, s, t;", + "\tvector lvl, q, blk;", + "\tvector> adj;", + "\tvector> edges;", + "\tDinic(int n) : n(n + 2), s(n++), t(n++), q(n), adj(n) {}", + "\tint add(int u, int v, ll cap, ll flow = 0) {", + "\t\tadj[u].push_back({v, int(adj[v].size()), cap, cap - flow});", + "\t\tadj[v].push_back({u, int(adj[u].size()) - 1, 0, 0});", + "\t\tedges.emplace_back(u, adj[u].size() - 1);", + "\t\treturn edges.size() - 1; // this Edge's ID", + "\t}", + "\tll dfs(int u, ll in) {", + "\t\tif (u == t || !in) return in;", + "\t\tll flow = 0;", + "\t\tfor (auto& e : adj[u]) {", + "\t\t\tif (e.res && !blk[e.v] && lvl[e.v] == lvl[u] - 1)", + "\t\t\t\tif (ll out = dfs(e.v, min(in, e.res))) {", + "\t\t\t\t\tflow += out, in -= out, e.res -= out;", + "\t\t\t\t\tadj[e.v][e.dual].res += out;", + "\t\t\t\t\tif (!in) return flow;", + "\t\t\t\t}", + "\t\t}", + "\t\tblk[u] = 1;", + "\t\treturn flow;", + "\t}", + "\tll flow() {", + "\t\tll flow = 0;", + "\t\tq[0] = t;", + "\t\tfor (int B = SCALING * 30; B >= 0; B--) do {", + "\t\t\t\tlvl = blk = vector(n);", + "\t\t\t\tint qi = 0, qe = lvl[t] = 1;", + "\t\t\t\twhile (qi < qe && !lvl[s]) {", + "\t\t\t\t\tint u = q[qi++];", + "\t\t\t\t\tfor (auto& e : adj[u])", + "\t\t\t\t\t\tif (!lvl[e.v] && adj[e.v][e.dual].res >> B)", + "\t\t\t\t\t\t\tq[qe++] = e.v, lvl[e.v] = lvl[u] + 1;", + "\t\t\t\t}", + "\t\t\t\tif (lvl[s]) flow += dfs(s, LLONG_MAX);", + "\t\t\t} while (lvl[s]);", + "\t\treturn flow;", + "\t}", + "\tEdge& get(int id) { // get Edge object from its ID", + "\t\treturn adj[edges[id].first][edges[id].second];", + "\t}", + "\tvoid clear() {", + "\t\tfor (auto& it : adj)", + "\t\t\tfor (auto& e : it) e.res = e.cap;", + "\t}", + "\tbool leftOfMinCut(int u) { return lvl[u] == 0; }", + "\t// d is a list of vertex demands, d[u] = flow in - flow out", + "\t// negative if u is a source, positive if u is a sink", + "\tbool circulation(vector d = {}) {", + "\t\td.resize(n);", + "\t\tvector circEdges;", + "\t\tDinic g(n);", + "\t\tfor (int u = 0; u < n; u++)", + "\t\t\tfor (auto& e : adj[u]) {", + "\t\t\t\td[u] += e.flow(), d[e.v] -= e.flow();", + "\t\t\t\tif (e.res) circEdges.push_back(g.add(u, e.v, e.res));", + "\t\t\t}", + "\t\tint tylerEdge = g.add(t, s, LLONG_MAX, 0);", + "\t\tll flow = 0;", + "\t\tfor (int u = 0; u < n; u++)", + "\t\t\tif (d[u] < 0)", + "\t\t\t\tg.add(g.s, u, -d[u]);", + "\t\t\telse if (d[u] > 0)", + "\t\t\t\tg.add(u, g.t, d[u]), flow += d[u];", + "\t\tif (flow != g.flow()) return false;", + "\t\tint i = 0; // reconstruct the flow into this graph", + "\t\tfor (int u = 0; u < n; u++)", + "\t\t\tfor (auto& e : adj[u])", + "\t\t\t\tif (e.res) e.res -= g.get(circEdges[i++]).flow();", + "\t\treturn true;", + "\t}", + "};" + ], + "description": "Max flow algorithm. Can find a valid\ncirculation given vertex and/or edge demands. Time:\n$O(VE\\log{U})$", + "prefix": [ + "hack_Dinic" + ] + } +} \ No newline at end of file diff --git a/testing/graphs/Dinic_FASTFLOW.cpp b/testing/graphs/Dinic_FASTFLOW.cpp new file mode 100644 index 00000000..ed872836 --- /dev/null +++ b/testing/graphs/Dinic_FASTFLOW.cpp @@ -0,0 +1,33 @@ +// tested on SPOJ FASTFLOW +// https://www.spoj.com/problems/FASTFLOW/ +#include +#define all(x) begin(x), end(x) +using namespace std; +using ll = long long; + +#include "../../content/graphs/Dinic.h" + +int main() { + cin.tie(0)->sync_with_stdio(0); + cin.exceptions(cin.failbit); + + int n, m; + cin >> n >> m; + + Dinic g(n); + g.add(g.s, 0, LLONG_MAX / 4); + g.add(n - 1, g.t, LLONG_MAX / 4); + + for (int i = 0; i < m; i++) { + int a, b, c; + cin >> a >> b >> c; + a--, b--; + + g.add(a, b, c); + g.add(b, a, c); + } + + cout << g.flow() << '\n'; + + return 0; +} diff --git a/testing/graphs/Dinic_MATCHING.cpp b/testing/graphs/Dinic_MATCHING.cpp new file mode 100644 index 00000000..c8c891ba --- /dev/null +++ b/testing/graphs/Dinic_MATCHING.cpp @@ -0,0 +1,34 @@ +// tested on SPOJ MATCHING +// https://www.spoj.com/problems/MATCHING/ +#include +#define all(x) begin(x), end(x) +using namespace std; +using ll = long long; + +#include "../../content/graphs/Dinic.h" + +int main() { + cin.tie(0)->sync_with_stdio(0); + cin.exceptions(cin.failbit); + + int n1, n2, m; + cin >> n1 >> n2 >> m; + + Dinic g(n1 + n2); + + for (int i = 0; i < n1; i++) g.add(g.s, i, 1); + + for (int i = 0; i < n2; i++) g.add(n1 + i, g.t, 1); + + for (int i = 0; i < m; i++) { + int u, v; + cin >> u >> v; + u--, v--; + + g.add(u, n1 + v, 1); + } + + cout << g.flow() << '\n'; + + return 0; +} diff --git a/testing/graphs/Dinic_ReactorCooling.cpp b/testing/graphs/Dinic_ReactorCooling.cpp new file mode 100644 index 00000000..dcdb5ad6 --- /dev/null +++ b/testing/graphs/Dinic_ReactorCooling.cpp @@ -0,0 +1,39 @@ +// tested on ASC 1: Reactor Cooling +// https://codeforces.com/gym/100199 +#include +#define all(x) begin(x), end(x) +using namespace std; +using ll = long long; + +#include "../../content/graphs/Dinic.h" + +int main() { + cin.tie(0)->sync_with_stdio(0); + cin.exceptions(cin.failbit); + freopen("cooling.in", "r", stdin); + freopen("cooling.out", "w", stdout); + + int n, m; + cin >> n >> m; + + Dinic g(n); + vector ids; + + for (int i = 0; i < m; i++) { + int u, v, d, c; + cin >> u >> v >> d >> c; + u--, v--; + + ids.push_back(g.add(u, v, c, d)); + } + + if (!g.circulation()) { + cout << "NO\n"; + return 0; + } + + cout << "YES\n"; + for (int id : ids) cout << g.get(id).flow() << '\n'; + + return 0; +}