多路增广 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=rk 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 minflowr就是从 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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43701790/article/details/107933036
今日推荐