流网络(Flow Networks)指的是一个有向图 G = (V, E),其中每条边 (u, v) ∈ E 均有一非负容量 c(u, v) ≥ 0。如果 (u, v) ∉ E 则可以规定 c(u, v) = 0。流网络中有两个特殊的顶点:源点 s (source)和汇点 t(sink)。为方便起见,假定每个顶点均处于从源点到汇点的某条路径上,就是说,对每个顶点 v ∈ E,存在一条路径 s --> v --> t。因此,图 G 为连通图,且 |E| ≥ |V| - 1。

下图展示了一个流网络实例。

设 G = (V, E) 是一个流网络,其容量函数为 c。设 s 为网络的源点,t 为汇点。G 的流的一个实值函数 f:V×V → R,且满足下列三个性质:

  • 容量限制(Capacity Constraint):对所有顶点对 u, v ∈ V,要求 f(u, v) ≤ c(u, v)。
  • 反对称性(Skew Symmetry):对所有顶点对 u, v ∈ V,要求 f(u, v) = - f(v, u)。
  • 流守恒性(Flow Conservation):对所有顶点对 u ∈ V - {s, t},要求 Σv∈Vf(u, v) = 0。

f(u, v) 称为从顶点 u 到顶点 v 的流,流的值定义为:|f| =Σv∈Vf(s, v),即从源点 s 出发的总流。

最大流问题(Maximum-flow problem)中,给出源点 s 和汇点 t 的流网络 G,希望找出从 s 到 t 的最大值流。

满足流网络的性质的实际上定义了问题的限制:

  • 经过边的流不能超过边的容量;
  • 除了源点 s 和汇点 t,对于其它所有顶点,流入量与流出量要相等;

上面的图中描述的流网络可简化为下图,其中源点 s = 0,汇点 t = 5。

上图的最大流为 23,流向如下图所示。

Ford-Fulkerson 算法是一种解决最大流的方法,其依赖于三种重要思想:

  1. 残留网络(Residual networks)
  2. 增广路径(Augmenting paths)
  3. 割(Cut)

这些思想是最大流最小割定理的精髓,该定理用流网络的割来描述最大流的值。

最大流最小割定理

如果 f 是具有源点 s 和汇点 t 的流网络 G = (V, E) 中的一个流,则下列条件是等价的:

  1. f 是 G 的一个最大流。
  2. 残留网络 Gf 不包含增广路径。
  3. 对 G 的某个割 (S, T),有 |f| = c(S, T)。

Ford-Fulkerson 算法是一种迭代方法。开始时,对所有 u, v ∈ V 有 f(u, v) = 0,即初始状态时流的值为 0。在每次迭代中,可通过寻找一条增广路径来增加流值。增广路径可以看做是从源点 s 到汇点 t 之间的一条路径,沿该路径可以压入更多的流,从而增加流的值。反复进行这一过程,直至增广路径都被找出为止。最大流最小割定理将说明在算法终止时,这一过程可产生出最大流。

 FORD-FULKERSON-METHOD(G, s, t)
initialize flow f to
while there exists an augmenting path p
do augment flow f along p
return f

上述伪码实现的时间复杂度为 O(max_flow * E)。

C# 代码实现如下:

 using System;
using System.Collections.Generic;
using System.Linq; namespace GraphAlgorithmTesting
{
class Program
{
static void Main(string[] args)
{
Graph g = new Graph();
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , ); Console.WriteLine();
Console.WriteLine("Graph Vertex Count : {0}", g.VertexCount);
Console.WriteLine("Graph Edge Count : {0}", g.EdgeCount);
Console.WriteLine(); int maxFlow = g.FordFulkerson(, );
Console.WriteLine("The Max Flow is : {0}", maxFlow); Console.ReadKey();
} class Edge
{
public Edge(int begin, int end, int weight)
{
this.Begin = begin;
this.End = end;
this.Weight = weight;
} public int Begin { get; private set; }
public int End { get; private set; }
public int Weight { get; private set; } public override string ToString()
{
return string.Format(
"Begin[{0}], End[{1}], Weight[{2}]",
Begin, End, Weight);
}
} class Graph
{
private Dictionary<int, List<Edge>> _adjacentEdges
= new Dictionary<int, List<Edge>>(); public Graph(int vertexCount)
{
this.VertexCount = vertexCount;
} public int VertexCount { get; private set; } public IEnumerable<int> Vertices
{
get
{
return _adjacentEdges.Keys;
}
} public IEnumerable<Edge> Edges
{
get
{
return _adjacentEdges.Values.SelectMany(e => e);
}
} public int EdgeCount
{
get
{
return this.Edges.Count();
}
} public void AddEdge(int begin, int end, int weight)
{
if (!_adjacentEdges.ContainsKey(begin))
{
var edges = new List<Edge>();
_adjacentEdges.Add(begin, edges);
} _adjacentEdges[begin].Add(new Edge(begin, end, weight));
} public int FordFulkerson(int s, int t)
{
int u, v; // Create a residual graph and fill the residual graph with
// given capacities in the original graph as residual capacities
// in residual graph
int[,] residual = new int[VertexCount, VertexCount]; // Residual graph where rGraph[i,j] indicates
// residual capacity of edge from i to j (if there
// is an edge. If rGraph[i,j] is 0, then there is not)
for (u = ; u < VertexCount; u++)
for (v = ; v < VertexCount; v++)
residual[u, v] = ;
foreach (var edge in this.Edges)
{
residual[edge.Begin, edge.End] = edge.Weight;
} // This array is filled by BFS and to store path
int[] parent = new int[VertexCount]; // There is no flow initially
int maxFlow = ; // Augment the flow while there is path from source to sink
while (BFS(residual, s, t, parent))
{
// Find minimum residual capacity of the edhes along the
// path filled by BFS. Or we can say find the maximum flow
// through the path found.
int pathFlow = int.MaxValue;
for (v = t; v != s; v = parent[v])
{
u = parent[v];
pathFlow = pathFlow < residual[u, v]
? pathFlow : residual[u, v];
} // update residual capacities of the edges and reverse edges
// along the path
for (v = t; v != s; v = parent[v])
{
u = parent[v];
residual[u, v] -= pathFlow;
residual[v, u] += pathFlow;
} // Add path flow to overall flow
maxFlow += pathFlow;
} // Return the overall flow
return maxFlow;
} // Returns true if there is a path from source 's' to sink 't' in
// residual graph. Also fills parent[] to store the path.
private bool BFS(int[,] residual, int s, int t, int[] parent)
{
bool[] visited = new bool[VertexCount];
for (int i = ; i < visited.Length; i++)
{
visited[i] = false;
} Queue<int> q = new Queue<int>(); visited[s] = true;
q.Enqueue(s);
parent[s] = -; // standard BFS loop
while (q.Count > )
{
int u = q.Dequeue(); for (int v = ; v < VertexCount; v++)
{
if (!visited[v]
&& residual[u, v] > )
{
q.Enqueue(v);
visited[v] = true;
parent[v] = u;
}
}
} // If we reached sink in BFS starting from source,
// then return true, else false
return visited[t] == true;
}
}
}
}

运行结果如下:

参考资料

本篇文章《Ford-Fulkerson 最大流算法》由 Dennis Gao 发表自博客园,未经作者本人同意禁止任何形式的转载,任何自动或人为的爬虫转载行为均为耍流氓。

Ford-Fulkerson 最大流算法的更多相关文章

  1. ACM/ICPC 之 网络流入门-Ford Fulkerson与SAP算法(POJ1149-POJ1273)

    第一题:按顾客访问猪圈的顺序依次构图(顾客为结点),汇点->第一个顾客->第二个顾客->...->汇点 //第一道网络流 //Ford-Fulkerson //Time:47M ...

  2. 算法9-5:最大流算法的Java代码

    残留网络 在介绍最大流算法之前先介绍一下什么是残留网络.残余网络的概念有点类似于集合中的补集概念. 下图是残余网络的样例. 上面的网络是原始网络.以下的网络是计算出的残留网络.残留网络的作用就是用来描 ...

  3. 海量数据挖掘MMDS week3:流算法Stream Algorithms

    http://blog.csdn.net/pipisorry/article/details/49183379 海量数据挖掘Mining Massive Datasets(MMDs) -Jure Le ...

  4. 基于.net的分布式系统限流组件(限流算法:令牌算法和漏斗算法)

    转载链接:https://www.cnblogs.com/vveiliang/p/9049393.html 1.令牌桶算法 令牌桶算法是比较常见的限流算法之一,大概描述如下: 1).所有的请求在处理之 ...

  5. 最大流算法-ISAP

    引入 最大流算法分为两类,一种是增广路算法,一种是预留推进算法.增广路算法包括时间复杂度\(O(nm^2)\)的EK算法,上界为\(O(n^2m)\)的Dinic算法,以及一些其他的算法.EK算法直接 ...

  6. 常用限流算法与Guava RateLimiter源码解析

    在分布式系统中,应对高并发访问时,缓存.限流.降级是保护系统正常运行的常用方法.当请求量突发暴涨时,如果不加以限制访问,则可能导致整个系统崩溃,服务不可用.同时有一些业务场景,比如短信验证码,或者其它 ...

  7. Cable TV Network 顶点连通度 (最大流算法)

    Cable TV Network 题目抽象:给出含有n个点顶点的无向图,给出m条边.求定点联通度   K 算法:将每个顶点v拆成 v'   v''  ,v'-->v''的容量为1.       ...

  8. 最大流算法(Edmons-Karp + Dinic 比较) + Ford-Fulkson 简要证明

    Ford-Fulkson用EK实现:483ms #include <cstdio> #include <cstring> #define min(x,y) (x>y?y: ...

  9. 最大流算法----(SAP 和 EK)

    EK算法的核心 反复寻找源点 s 到汇点 t 之间的增广路径,若有,找出增广路径上每一段的最小值delta,若无,则结束. 寻找增广路径时用BFS来找,并且更新残留网的值. 找到delta后,则使最大 ...

随机推荐

  1. C# DateTime与时间戳转换

    C# DateTime与时间戳的相互转换,包括JavaScript时间戳和Unix的时间戳. 1. 什么是时间戳 首先要清楚JavaScript与Unix的时间戳的区别: JavaScript时间戳: ...

  2. 使用 Nodejs 搭建简单的Web服务器

    使用Nodejs搭建Web服务器是学习Node.js比较全面的入门教程,因为要完成一个简单的Web服务器,你需要学习Nodejs中几个比较重要的模块,比如:http协议模块.文件系统.url解析模块. ...

  3. Hyper-V2:向VM增加虚拟硬盘

    使用Hyper-V创建VM,在VM成功安装OS之后,发现VM只有一个逻辑盘C,用于存储VM的操作系统.在产品环境中,需要向VM增加虚拟硬盘,便于将数据单独存储在不同的逻辑盘符中.在Hyper-V中,分 ...

  4. webpack的使用

    1.webpack是什么? 打包前端项目的工具(为项目提高逼格的东西). 2.webpack的基本命令 webpack#最基本的启动webpack命令 webpack-w #提供watch方法,实时进 ...

  5. 浅谈 LayoutInflater

    浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...

  6. webix前端架构的项目应用

    webix框架兼容javascript.HTML.CSS,应用比较灵活,应用框架时,配合后台webAPI,整个web项目里面,App文件夹保存前台的多语言文件,图片文件,webix原代码js.css, ...

  7. 拦截UIViewController的popViewController事件

    实现拦截UIViewController的pop操作有两种方式: 自定义实现返回按钮,即设置UIBarButtonItem来实现自定义的返回操作. 创建UINavigatonController的Ca ...

  8. SVN的使用

  9. MySQL 数据库双向同步复制

    MySQL 复制问题的最后一篇,关于双向同步复制架构设计的一些设计要点与制约. 问题和制约 数据库的双主双写并双向同步场景,主要考虑数据完整性.一致性和避免冲突.对于同一个库,同一张表,同一个记录中的 ...

  10. Linux基础介绍【第四篇】

    Linux文件和目录的属性及权限 命令: [root@oldboy ~]# ls -lhi total 40K 24973 -rw-------. 1 root root 1.1K Dec 10 16 ...