网络最大流[dinic]
模板题:P3376 【模板】网络最大流
视频讲解
核心:
\(bfs\) 寻找增广路并对图进行分层,要严格按照深度递增,这样可以降低 \(dfs\)搜索深度,\(dfs\) 求增广路最大流量。
优化:
概要:
- 当前弧优化,当前增广路寻找最大流,之前增广路搜过的边都被榨干了,可以不要了(小优化);
- 多路增广:到达每一个节点,都尽可能的增广,直到增广到汇点;
- 炸点:若点在 \(dfs\) 中无法返回值,则意味着它被榨干了,就炸掉它;
详解:
- 当前弧优化:for. e.g. 中间节点 \(A\),有三个流向,\(A\)->\(B\),\(A\)->\(C\),\(A\)->\(D\),在 \(A\) 点时,如果 \(A\) -> \(B\),\(A\)->\(C\) 这两个边都已经满流,那么下次再经过 \(A\) 节点时,直接跳过 \(A\)->\(B\),\(A\)->\(C\) 这两条边,直接访问 \(A\)->\(C\) 这个点,这个就是当前弧优化,实现方式,可开一个数组记录每个节点访问到第几条边。
- 多路增广:到达每一个节点,都尽可能的增广,直到增广到汇点。
- 炸点:一次分层,多次寻找增广路线,在一次寻找增广路线中,发现中间节点 \(A\),有三个流向,\(A\)->\(B\),\(A\)->\(C\),\(A\)->\(D\),这个三个流向都满流了,那么这个节点就不能作为中间节点出现了,因为它的流向都已经作为增广路线,全部占满了。具体操作为 \(depth_A = 0\)。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define MAXN 205
#define MAXM 5005
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll inf = 1 << 30;
int n, m, s, t;
struct edge
{
int v, nxt;
long long w;
}G[MAXM << 1];
int head[MAXN], cntEdge = 1, cur[MAXN], depth[MAXN];
ull maxFlow = 0;
ull read()
{
ull x = 0; char ch = getchar();
while (ch < '0' || ch > '9')
ch = getchar();
while (ch >= '0' && ch <= '9')
{
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return x;
}
inline void addEdge(int u, int v, int w)
{
++cntEdge;
G[cntEdge].v = v;
G[cntEdge].w = w;
G[cntEdge].nxt = head[u];
head[u] = cntEdge;
}
void init()
{
n = read(), m = read(), s = read(), t = read();
for (int i = 1; i <= m; ++i)
{
int u = read(), v = read(), w = read();
addEdge(u, v, w);
addEdge(v, u, 0); // 建反边
}
}
bool bfs() // 判断是否存在增广路并构造增广路
{
memset(depth, 0, sizeof(depth));
queue <int> q;
q.push(s); depth[s] = 1;
cur[s] = head[s];
while (!q.empty())
{
int u = q.front(); q.pop();
for (int i = head[u]; i; i = G[i].nxt)
{
if (G[i].w && !depth[G[i].v])
{
depth[G[i].v] = depth[u] + 1;
cur[G[i].v] = head[G[i].v];
q.push(G[i].v);
if (G[i].v == t) // 只用找到一条增广路就行了
return 1;
}
}
}
return depth[t];
}
ll dfs(int u, ll flow)
{
if (u == t) return flow; // 搜索边界:到达汇点
ll ret = 0;
for (int i = cur[u]; i && flow; i = G[i].nxt) // flow 用完了要及时退出循环
{
cur[u] = i; // 当前弧优化灵魂,之前增广路搜过的边都被榨干了,可以不要了
if (G[i].w && depth[G[i].v] == depth[u] + 1) // 由于有反边,所以要向深度更大的点搜索
{
ll x = dfs(G[i].v, min(flow, G[i].w));
G[i].w -= x, G[i ^ 1].w += x; // 反边的灵魂:用了多少,就给反边加上多少
flow -= x, ret += x;
}
}
if (!ret) depth[u] = 0; // 该点被榨干了,炸掉它
return ret;
}
void solve()
{
while (bfs())
maxFlow += dfs(s, inf); // 源点无线流量
printf("%llu", maxFlow);
}
int main()
{
init();
solve();
return 0;
}

浙公网安备 33010602011771号