通俗讲解Ford-Fulkerson算法求最大流Max Flow

什么是流网络

流网络是一种特殊的单向有权连通图。有且只有一个源点和汇点,权重作为容量。

 图中a点是源点,也就是起点,一般记作s,f是汇点,也就是终点,一般记作t。

通过以上流网络衍生出来的,满足每条边的容量约束并且每个节点流入流出的流量守恒的图成为一个流(Flow)。

最大流问题就是求解给定流网络的最大流量配置,也就是怎样分配每天表上的流量能让源点到汇点的流量最大。上面在个流就不最大流,下面的流是最大流。

 

 Ford-Fulkerson算法是求解最大流的常用算法,具体步骤如下:

首先将流网络复制两份,一份所有权重清零,用作记录当前求得的流,另一方完全复制原始网络,成为残存网络。

 在当前残存网络中用BFS或DFS搜出任意一条从源点到汇点的路径,成为增广路径,路径的流量使用组成这条路径所有边的最小流量,也就是瓶颈或短板。然后把这条路径的流量叠加到当前流中,同时在残存网络中减掉。

继续在更新后的残存网络中搜索增广路径,直到无法搜到新的路径,因为部分关键边的容量已经用尽。这个时候当前流就是在最终的流。

但是,最终的流不一定是最大的,例如上面黄色的最终流总流量是9。我们还可以有总流量为12的流。

 

 这个算法最核心,最巧妙的部分就在下面:

建立残存网络的时候,建立双向图,反向的边初始化容量都为0,其他不变。

 当一条增广路径从残存网络中减掉的时候,每条边正向减去多少,就在反向边上加多少。

 这时在更新后的残存网络中搜索新的增广路径,有可能用上反向路径。减去反向流量的时候会使之前已经消耗的正向流量增加,起到撤销之前路径的作用。流量往当前图叠加的时候,要注意方向,如果是反向的,要加负号也就是减去。

通过这种方式得到的最终流就是最大流。

至于为什么这样做是有效的,有数学严格证明是很复杂的。我们可以只观地看,无非就是验证三个问题:

添加不存在的路径为什么会通?

流量撤销之后,原来的路径为什么还通?

最终流量为什么是最大的?

用文字很难写出来,感兴趣的可以在B站搜这篇文章的同名视频。

有了上面的思路,有编程基础的就可以把程序写出来了,编程基础不好的可以找我学编程写程序。

 

  • 4
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Ford-Fulkerson算法最大流问题的一种经典算法。以下是一个基于增广路思想的Ford-Fulkerson算法的代码: ``` // 基于邻接矩阵的Ford-Fulkerson算法实现 #include <iostream> #include <queue> #include <cstring> using namespace std; const int MAXN = 100; // 最大顶点数 const int INF = 0x3f3f3f3f; // 表示无穷大 int n, m; // n表示顶点数,m表示边数 int s, t; // s表示源点,t表示汇点 int cap[MAXN][MAXN]; // 表示容量 int flow[MAXN][MAXN]; // 表示流量 int pre[MAXN]; // 表示前驱节点 int bfs() { memset(pre, -1, sizeof(pre)); // 初始化前驱节点数组 queue<int> q; q.push(s); pre[s] = -2; while (!q.empty()) { int u = q.front(); q.pop(); for (int v = 0; v < n; ++v) { if (pre[v] == -1 && cap[u][v] > flow[u][v]) { pre[v] = u; if (v == t) return 1; q.push(v); } } } return 0; } int maxFlow() { int ans = 0; while (bfs()) { int minflow = INF; for (int u = t; u != s; u = pre[u]) { int v = pre[u]; minflow = min(minflow, cap[v][u] - flow[v][u]); } for (int u = t; u != s; u = pre[u]) { int v = pre[u]; flow[v][u] += minflow; flow[u][v] -= minflow; } ans += minflow; } return ans; } int main() { cin >> n >> m >> s >> t; memset(cap, 0, sizeof(cap)); memset(flow, 0, sizeof(flow)); for (int i = 0; i < m; ++i) { int u, v, c; cin >> u >> v >> c; cap[u][v] += c; // 注意有可能存在重边 } cout << maxFlow() << endl; return 0; } ``` 算法思路: 1. 初始化流量为0; 2. 在剩余容量大于0的情况下,寻找增广路: - 从源点s开始,使用BFS寻找一条增广路; - 如果找到增广路,计算增广路上的最小剩余容量minflow,更新流量; 3. 最大流就是所有增广路上的最小剩余容量之和。 其中,增广路的定义是指从源点到汇点路径上,剩余容量均大于0的路径。在Ford-Fulkerson算法中,每次都需要寻找一条增广路来更新流量,直到无法再找到增广路为止。这个过程中,每次找到的增广路都可以使得流量增加,因此最终的流量是不断增加的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小艾米编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值