Ford-Fulkerson 最大流算法

流网络(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 之间的一条路径,沿该路径可以压入更多的流,从而增加流的值。反复进行这一过程,直至增广路径都被找出为止。最大流最小割定理将说明在算法终止时,这一过程可产生出最大流。

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

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

C# 代码实现如下:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4
  5 namespace GraphAlgorithmTesting
  6 {
  7   class Program
  8   {
  9     static void Main(string[] args)
 10     {
 11       Graph g = new Graph(6);
 12       g.AddEdge(0, 1, 16);
 13       g.AddEdge(0, 2, 13);
 14       g.AddEdge(1, 2, 10);
 15       g.AddEdge(1, 3, 12);
 16       g.AddEdge(2, 1, 4);
 17       g.AddEdge(2, 4, 14);
 18       g.AddEdge(3, 2, 9);
 19       g.AddEdge(3, 5, 20);
 20       g.AddEdge(4, 3, 7);
 21       g.AddEdge(4, 5, 4);
 22
 23       Console.WriteLine();
 24       Console.WriteLine("Graph Vertex Count : {0}", g.VertexCount);
 25       Console.WriteLine("Graph Edge Count : {0}", g.EdgeCount);
 26       Console.WriteLine();
 27
 28       int maxFlow = g.FordFulkerson(0, 5);
 29       Console.WriteLine("The Max Flow is : {0}", maxFlow);
 30
 31       Console.ReadKey();
 32     }
 33
 34     class Edge
 35     {
 36       public Edge(int begin, int end, int weight)
 37       {
 38         this.Begin = begin;
 39         this.End = end;
 40         this.Weight = weight;
 41       }
 42
 43       public int Begin { get; private set; }
 44       public int End { get; private set; }
 45       public int Weight { get; private set; }
 46
 47       public override string ToString()
 48       {
 49         return string.Format(
 50           "Begin[{0}], End[{1}], Weight[{2}]",
 51           Begin, End, Weight);
 52       }
 53     }
 54
 55     class Graph
 56     {
 57       private Dictionary<int, List<Edge>> _adjacentEdges
 58         = new Dictionary<int, List<Edge>>();
 59
 60       public Graph(int vertexCount)
 61       {
 62         this.VertexCount = vertexCount;
 63       }
 64
 65       public int VertexCount { get; private set; }
 66
 67       public IEnumerable<int> Vertices
 68       {
 69         get
 70         {
 71           return _adjacentEdges.Keys;
 72         }
 73       }
 74
 75       public IEnumerable<Edge> Edges
 76       {
 77         get
 78         {
 79           return _adjacentEdges.Values.SelectMany(e => e);
 80         }
 81       }
 82
 83       public int EdgeCount
 84       {
 85         get
 86         {
 87           return this.Edges.Count();
 88         }
 89       }
 90
 91       public void AddEdge(int begin, int end, int weight)
 92       {
 93         if (!_adjacentEdges.ContainsKey(begin))
 94         {
 95           var edges = new List<Edge>();
 96           _adjacentEdges.Add(begin, edges);
 97         }
 98
 99         _adjacentEdges[begin].Add(new Edge(begin, end, weight));
100       }
101
102       public int FordFulkerson(int s, int t)
103       {
104         int u, v;
105
106         // Create a residual graph and fill the residual graph with
107         // given capacities in the original graph as residual capacities
108         // in residual graph
109         int[,] residual = new int[VertexCount, VertexCount];
110
111         // Residual graph where rGraph[i,j] indicates
112         // residual capacity of edge from i to j (if there
113         // is an edge. If rGraph[i,j] is 0, then there is not)
114         for (u = 0; u < VertexCount; u++)
115           for (v = 0; v < VertexCount; v++)
116             residual[u, v] = 0;
117         foreach (var edge in this.Edges)
118         {
119           residual[edge.Begin, edge.End] = edge.Weight;
120         }
121
122         // This array is filled by BFS and to store path
123         int[] parent = new int[VertexCount];
124
125         // There is no flow initially
126         int maxFlow = 0;
127
128         // Augment the flow while there is path from source to sink
129         while (BFS(residual, s, t, parent))
130         {
131           // Find minimum residual capacity of the edhes along the
132           // path filled by BFS. Or we can say find the maximum flow
133           // through the path found.
134           int pathFlow = int.MaxValue;
135           for (v = t; v != s; v = parent[v])
136           {
137             u = parent[v];
138             pathFlow = pathFlow < residual[u, v]
139               ? pathFlow : residual[u, v];
140           }
141
142           // update residual capacities of the edges and reverse edges
143           // along the path
144           for (v = t; v != s; v = parent[v])
145           {
146             u = parent[v];
147             residual[u, v] -= pathFlow;
148             residual[v, u] += pathFlow;
149           }
150
151           // Add path flow to overall flow
152           maxFlow += pathFlow;
153         }
154
155         // Return the overall flow
156         return maxFlow;
157       }
158
159       // Returns true if there is a path from source ‘s‘ to sink ‘t‘ in
160       // residual graph. Also fills parent[] to store the path.
161       private bool BFS(int[,] residual, int s, int t, int[] parent)
162       {
163         bool[] visited = new bool[VertexCount];
164         for (int i = 0; i < visited.Length; i++)
165         {
166           visited[i] = false;
167         }
168
169         Queue<int> q = new Queue<int>();
170
171         visited[s] = true;
172         q.Enqueue(s);
173         parent[s] = -1;
174
175         // standard BFS loop
176         while (q.Count > 0)
177         {
178           int u = q.Dequeue();
179
180           for (int v = 0; v < VertexCount; v++)
181           {
182             if (!visited[v]
183               && residual[u, v] > 0)
184             {
185               q.Enqueue(v);
186               visited[v] = true;
187               parent[v] = u;
188             }
189           }
190         }
191
192         // If we reached sink in BFS starting from source,
193         // then return true, else false
194         return visited[t] == true;
195       }
196     }
197   }
198 }

运行结果如下:

参考资料

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

时间: 2024-10-14 21:03:04

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

C++ 基于Dijkstra最短路搜索的Ford Fulkson最大流算法

#include<iostream> #include<cstdlib> #include<cstdio> #include<ctime> #include<cstring> using namespace std; const int MAXN = 120; const int INF = INT_MAX; int G[MAXN][MAXN], N; int dist[MAXN], Pre[MAXN]; bool visited[MAXN];

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

Ford-Fulkson用EK实现:483ms #include <cstdio> #include <cstring> #define min(x,y) (x>y?y:x) int pre[105],q[105]; int F[105][105]; int n,nc,np,m,s,t,all; int MaxFlow(int s, int t){ int ans=0; while(1){ memset(pre,0,sizeof(pre)); int head=0,tail=

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

Cable TV Network 题目抽象:给出含有n个点顶点的无向图,给出m条边.求定点联通度   K 算法:将每个顶点v拆成 v'   v''  ,v'-->v''的容量为1.           对于原图中的边(u,v)   连边   u''--->v'    v''-->u'.    求每对定点的P(u,v);以u为源点,v为汇点. 我们只需固定一个顶点,枚举其它汇点. 1 #include <iostream> 2 #include <cstdio> 3

算法9-4:最大流算法复杂度分析

前面一节介绍了Ford-Fulkerson算法.那么这个算法是否一定能够在有限步骤内结束?要多少步骤呢? 这个问题的答案是,该算法确实能够在有限步骤之内结束,但是至于需要多少步骤,就要仔细分析. 为了分析问题,需要假定图中所有边的容量都是整数.但是有个严重的问题,比如下图中,如果使用Ford-Fulkerson算法,需要迭代200次才能结束. 首先将所有边的容量都初始化为0. 第一次迭代和第二次迭代之后,两条边各增加了1. 到最后200次迭代之后整个算法才结束. 这还不算最坏的情况.因为整数最多

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

残留网络 在介绍最大流算法之前先介绍一下什么是残留网络.残余网络的概念有点类似于集合中的补集概念. 下图是残余网络的例子.上面的网络是原始网络,下面的网络是计算出的残留网络.残留网络的作用就是用来描述这个网络中还剩下多少可以利用的流量. 流量网络 最大流算法比以前介绍的算法都要复杂.网络中的每一条边需要记录容量和当前流量.容量是固定值,是已知条件,而当前流量在计算过程中会一直发生变化.因此,需要建立一个专门的类,用于最大流算法. public class FlowEdge { private i

ACM/ICPC 之 网络流入门-Ford Fulkerson(POJ1149)

按顾客访问猪圈的顺序依次构图(顾客为结点),汇点->第一个顾客->第二个顾客->...->汇点 //第一道网络流 //Ford-Fulkerson //Time:47Ms Memory:276K #include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<queue> using namespace std; #def

接口限流算法总结

背景 曾经在一个大神的博客里看到这样一句话:在开发高并发系统时,有三把利器用来保护系统:缓存.降级和限流.那么何为限流呢?顾名思义,限流就是限制流量,就像你宽带包了1个G的流量,用完了就没了.通过限流,我们可以很好地控制系统的qps,从而达到保护系统的目的.本篇文章将会介绍一下常用的限流算法以及他们各自的特点. 算法介绍 计数器法 计 数器法是限流算法里最简单也是最容易实现的一种算法.比如我们规定,对于A接口来说,我们1分钟的访问次数不能超过100个.那么我们可以这么做:在一开 始的时候,我们可

常用的限流算法

常用的限流算法大致有三种:令牌桶算法,漏桶算法,计数器算法 令牌桶算法 令牌桶算法是一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌.令牌桶算法的描述如下: 1.假设限制2r/s,则按照500毫秒的固定速率往桶中添加令牌 2.桶中最多存放b个令牌,当桶满时,新添加的令牌被丢弃或拒绝 3.当一个n个字节大小的数据包到达,将从桶中删除n个令牌,接着数据包被发送到网络上 4.如果桶中的令牌不足n个,则不会删除令牌,且该数据包将被限流(要么丢弃,要么缓冲区等待) 漏桶算法 漏桶作为计量工具(The

限流算法

常见的限流算法有:令牌桶.漏桶.计数器. 令牌桶限流 令牌桶是一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌,填满了就丢弃令牌,请求是否被处理要看桶中令牌是否足够,当令牌数减为零时则拒绝新的请求.令牌桶允许一定程度突发流量,只要有令牌就可以处理,支持一次拿多个令牌.令牌桶中装的是令牌. 漏桶限流 一个固定容量的漏桶,按照固定常量速率流出请求,流入请求速率任意,当流入的请求数累积到漏桶容量时,则新流入的请求被拒绝.漏桶可以看做是一个具有固定容量.固定流出速率的队列,漏桶限制的是请求的流出速率