多路增广 Dinic算法
才疏学浅,不妥之处,敬请斧正。
E
K
EK
EK最短增广路算法中每次只增广一条,
D
i
n
i
c
Dinic
Dinic算法则多路增广。
D
i
n
i
c
Dinic
Dinic算法首先需要建立分层图,准确的说是每次多路增广前都要建立分层图,可以在残量网络上进行
b
f
s
bfs
bfs构建分层图。
分层图:满足
d
[
x
]
+
1
=
d
[
y
]
d[x]+1=d[y]
d[x]+1=d[y]的边
(
x
,
y
)
(x,y)
(x,y)构成的子图被称为分层图。
然后在分成图上
D
F
S
DFS
DFS寻找增广路,回溯是更新残量。直到残量网络中源点
S
S
S无法到达汇点
T
T
T。
b
f
s
bfs
bfs求层次、构建分层图的时候,如果残量网络中当前的边的容量为0,则不需要对其求层次,因为没有意义。同样,求过层次的点不需要再求一次。
每次多路增广之前都需要求一次层次,因为同一个点,可能因为和它连接的点的失效而导致层次不同。
d
i
n
i
c
(
x
,
f
l
o
w
)
dinic(x,flow)
dinic(x,flow)表示当前节点是
x
x
x并且当前增广路上的最大流量是
f
l
o
w
flow
flow的情况下最大的流量,相当于对
x
x
x的子节点都进行了增广,直到在当前残量网络上无法从
x
x
x开始找到一条增广路为止。
如果当前节点就是汇点,显然
f
l
o
w
flow
flow就是可以可以增加的流量,直接返回即可。
如果不是汇点,则递归的处理对其子节点进行增广。
int dinic(int x, int min_flow){
// x表示当前结点
// min_flow表示当前增广路上的最小流量
if (x == t)return min_flow;
int r, k;
r = min_flow;
for (int i = head[x]; ~i; i = nex[i]){
int y = to[i];
if (ed[i] && d[y] == d[x] + 1){
k = dinic(y, min(min_flow, ed[i]));
if (!k) d[y] = 0;
ed[i] -= k;
ed[i ^ 1] += k;
r -= k;
}
}
return min_flow - r;
}
上述代码中的
int k = dinic(y, min(min_flow, ed[i]));
表示求
x
x
x的子节点
y
y
y在前面的最小流量为
m
i
n
f
l
o
w
minflow
minflow的情况下,最多可以增加多少流量。
代码中的
r
r
r表示的是
x
x
x结点的残量,每次有一个子节点完成了增广,则残量
r
=
r
−
k
r=r-k
r=r−k,
m
i
n
f
l
o
w
minflow
minflow是从源点流向当前节点
x
x
x的增广路上的最小流量,
r
r
r一开始与
m
i
n
f
l
o
w
minflow
minflow相等,每从
x
x
x出发成功增广一次,
r
r
r就减小一点,减小的部分就是此次增广增加的流量,因此全部完成增广后,
m
i
n
f
l
o
w
−
r
minflow-r
minflow−r就是从
x
x
x开始的多条增广路最大可以增加的流量。
至于有些人这样的写法,我个人的理解是有些多余,但是也没有什么影响,因为从源点 s s s开始增广,结束的时候应该是完成了整个残量网络上的增广,因此不写第二个while也是可以的。
while (bfs()){
int num;
while ((num = dinic(s, inf)))max_flow += num;
}
// dinic
#include <bits/stdc++.h>
#define mem(a, b) memset(a, b, sizeof a)
using namespace std;
const int N = 310;
const int inf = 0x3f3f3f3f;
int head[N], nex[N], to[N], ed[N], cnt;
inline void add(int a, int b, int c){
to[++cnt] = b, nex[cnt] = head[a], head[a] = cnt, ed[cnt] = c;
to[++cnt] = a, nex[cnt] = head[b], head[b] = cnt, ed[cnt] = 0;
}
int d[N];
int n, m, s, t;
queue<int > q;
bool bfs(){// 构建分层图
while (q.size())q.pop();
mem(d, 0);
d[s] = 1;
q.push(s);
while (q.size()){
int top = q.front();
q.pop();
for (int i = head[top]; ~i; i = nex[i]){
int y = to[i];
if (ed[i] && !d[y]){
d[y] = d[top] + 1;
q.push(y);
if (y == t)return 1;
}
}
}
return 0;
}
int dinic(int x, int min_flow){
// x表示当前结点
// min_flow表示当前增广路上的最小流量
if (x == t)return min_flow;
int r, k;
r = min_flow;
for (int i = head[x]; ~i; i = nex[i]){
int y = to[i];
if (ed[i] && d[y] == d[x] + 1){
k = dinic(y, min(min_flow, ed[i]));
if (!k) d[y] = 0;
ed[i] -= k;
ed[i ^ 1] += k;
r -= k;
}
}
return min_flow - r;
}
void pre_work(){
mem(head, -1);
mem(nex, -1);
cnt = 1;
}
int main(){
freopen("in.in", "r", stdin);
ios::sync_with_stdio(0);
pre_work();
cin >> n >> m >> s >> t;
while (m--){
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
}
int max_flow = 0;
while (bfs()){
max_flow += dinic(s, inf);
}
cout << max_flow << "\n";
return 0;
}
本文来自博客园,作者:correct,转载请注明原文链接:https://www.cnblogs.com/correct/p/16548409.html

浙公网安备 33010602011771号