Vlad/Graph_Flow.cpp
2026-09-15 00:25:10 +05:00

124 lines
3.6 KiB
C++

#include "Graph.h"
#include <queue>
#include <map>
#include <algorithm>
#include <functional>
#include <limits>
namespace {
struct FlowEdge {
int to;
long long cap;
int rev;
};
using Residual = std::vector<std::vector<FlowEdge>>;
void addArc(Residual& g, int u, int v, long long cap) {
FlowEdge a{v, cap, (int)g[v].size()};
FlowEdge b{u, 0, (int)g[u].size()};
g[u].push_back(a);
g[v].push_back(b);
}
Residual buildResidual(int n, const std::vector<std::tuple<int, int, int>>& arcs) {
Residual g(n + 1);
for (auto& e : arcs) addArc(g, std::get<0>(e), std::get<1>(e), std::get<2>(e));
return g;
}
// Collapse parallel arcs by summing residual capacities so the result stays a simple graph.
Graph makeResidualGraph(int n, const Residual& g) {
std::map<std::pair<int, int>, long long> cap;
for (int u = 1; u <= n; ++u)
for (auto& e : g[u])
if (e.cap > 0) cap[{u, e.to}] += e.cap;
Graph res(n, true, true, 'E');
for (auto& pr : cap)
res.addEdge(pr.first.first, pr.first.second, (int)pr.second);
return res;
}
} // namespace
Graph Graph::flowFordFulkerson(int source, int sink) const {
Residual g = buildResidual(n, toEdgeList());
std::vector<int> parent(n + 1), parentEdge(n + 1);
while (true) {
std::fill(parent.begin(), parent.end(), -1);
parent[source] = source;
std::queue<int> q;
q.push(source);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = 0; i < (int)g[u].size(); ++i) {
const FlowEdge& e = g[u][i];
if (e.cap > 0 && parent[e.to] == -1) {
parent[e.to] = u;
parentEdge[e.to] = i;
q.push(e.to);
}
}
}
if (parent[sink] == -1) break;
long long bottleneck = std::numeric_limits<long long>::max();
for (int v = sink; v != source; v = parent[v])
bottleneck = std::min(bottleneck, g[parent[v]][parentEdge[v]].cap);
for (int v = sink; v != source; v = parent[v]) {
FlowEdge& e = g[parent[v]][parentEdge[v]];
e.cap -= bottleneck;
g[v][e.rev].cap += bottleneck;
}
}
return makeResidualGraph(n, g);
}
Graph Graph::flowDinitz(int source, int sink) const {
Residual g = buildResidual(n, toEdgeList());
std::vector<int> level(n + 1), ptr(n + 1);
auto bfs = [&]() -> bool {
std::fill(level.begin(), level.end(), -1);
level[source] = 0;
std::queue<int> q;
q.push(source);
while (!q.empty()) {
int u = q.front();
q.pop();
for (auto& e : g[u])
if (e.cap > 0 && level[e.to] == -1) {
level[e.to] = level[u] + 1;
q.push(e.to);
}
}
return level[sink] != -1;
};
std::function<long long(int, long long)> dfs = [&](int u, long long pushed) -> long long {
if (pushed == 0) return 0;
if (u == sink) return pushed;
for (int& i = ptr[u]; i < (int)g[u].size(); ++i) {
FlowEdge& e = g[u][i];
if (level[e.to] != level[u] + 1 || e.cap <= 0) continue;
long long pushedFlow = dfs(e.to, std::min(pushed, e.cap));
if (pushedFlow == 0) continue;
e.cap -= pushedFlow;
g[e.to][e.rev].cap += pushedFlow;
return pushedFlow;
}
return 0;
};
while (bfs()) {
std::fill(ptr.begin(), ptr.end(), 0);
while (dfs(source, std::numeric_limits<long long>::max()) > 0) {
}
}
return makeResidualGraph(n, g);
}