这篇博客这是记录网络流板子的,不进行网络流的讲解。用注释标注了一些易错或重要步骤。
Dinic(无当前弧优化)
dinic 的当前弧优化其实快不了多少,而且容易出事。
完整代码
#include <bits/stdc++.h>
using namespace std;
const int maxM = 1e5 + 3;
const int maxN = 1e4 + 3;
const int INF = 0x3f3f3f3f;
struct Edge {
int next, to, cap;
} edge[maxM << 1];
int head[maxN], lvl[maxN];
int n, m, cnt = -1, s, t; // cnt 需要初始化为奇数,因为需要用异或(^)表示反向边
void add(int from, int to, int cost) {
edge[++cnt] = (Edge) {head[from], to, cost};
head[from] = cnt;
}
void addEdge(int from, int to, int cost) {
add(from, to, cost);
add(to, from, 0);
}
queue<int> que;
bool bfs() { // BFS 标号过程
memset(lvl, -1, sizeof(lvl));
lvl[s] = 0;
que.push(s);
while (!que.empty()) {
int u = que.front();
que.pop();
for (int i = head[u]; ~i; i = edge[i].next) {
int v = edge[i].to;
if (lvl[v] < 0 && edge[i].cap) {
lvl[v] = lvl[u] + 1;
que.push(v);
}
}
}
return lvl[t] != -1;
}
int dfs(int u, int f) {
if (!f || u == t) return f;
int flow = 0;
for (int i = head[u]; ~i; i = edge[i].next) {
int v = edge[i].to;
if (lvl[v] == lvl[u] + 1 && edge[i].cap) {
int d = dfs(v, min(f, edge[i].cap));
flow += d;
f -= d;
edge[i].cap -= d;
edge[i ^ 1].cap += d;
}
}
if (!flow) lvl[u] = -1;
return flow;
}
int dinic() {
int ans = 0;
while (bfs()) ans += dfs(s, INF);
return ans;
}
int main() {
memset(head, -1, sizeof(head));
scanf("%d%d%d%d", &n, &m, &s, &t);
for (int i = 1; i <= m; i++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
addEdge(a, b, c);
}
printf("%d\n", dinic());
return 0;
}
最小费用最大流(SPFA 版)
就是把 dinic 算法中 BFS 标号过程魔改成了 SPFA,求最大流是根据进行 SPFA 所记录的路径进行的。
完整代码
#include <bits/stdc++.h>
using namespace std;
const int maxM = 5e4 + 3, maxN = 5e3 + 3;
const int INF = 0x7f7f7f7f;
struct Edge {
int next, to, cap, cost;
} edge[maxM << 1];
int head[maxN], dis[maxN], pre[maxN], preEdge[maxN];
bool vis[maxN];
int s, t, n, m, cnt = -1, minCost, maxFlow;
void add(int from, int to, int val, int cost) {
edge[++cnt] = (Edge) {head[from], to, val, cost};
head[from] = cnt;
}
void addEdge(int from, int to, int val, int cost) {
add(from, to, val, cost);
add(to, from, 0, -cost);
}
queue<int> que;
bool spfa() {
memset(dis, 0x7f, sizeof(dis));
memset(vis, false, sizeof(vis));
dis[s] = 0;
vis[s] = true;
que.push(s);
while (!que.empty()) {
int u = que.front();
que.pop();
vis[u] = false;
for (int i = head[u]; ~i; i = edge[i].next) {
int v = edge[i].to;
if (dis[v] > dis[u] + edge[i].cost && edge[i].cap) {
dis[v] = dis[u] + edge[i].cost;
pre[v] = u; // 用于记录路径,记录当前点所连的前一个点
preEdge[v] = i; // 记录路径,记录当前点所连的前一条边
if (!vis[v]) {
que.push(v);
vis[v] = true;
}
}
}
}
return dis[t] != INF;
}
void work() {
while (spfa()) {
int flow = INF;
for (int i = t; i != s; i = pre[i])
flow = min(flow, edge[preEdge[i]].cap);
maxFlow += flow;
minCost += dis[t] * flow;
for (int i = t; i != s; i = pre[i]) {
edge[preEdge[i]].cap -= flow;
edge[preEdge[i] ^ 1].cap += flow;
}
}
}
int main() {
memset(head, -1, sizeof(head));
scanf("%d%d%d%d", &n, &m, &s, &t);
for (int i = 1; i <= m; i++) {
int a, b, x, y;
scanf("%d%d%d%d", &a, &b, &x, &y);
addEdge(a, b, x, y);
}
work();
printf("%d\n", minCost);
return 0;
}