才疏学浅,不妥之处,敬请斧正。
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;
}