一、算法介绍
Ford-Fulkerson算法是一种最大流算法,其核心是通过引入“反向边”及“剩余图”的概念对原先的运输方案进行纠错、改进。引用卜东波老师“计算机算法设计与分析”讲义的描述如下:Ford-Fulkerson 算法引入反向边,让运出去的货物还可以有机会退回。将加入了退货边的图称为剩余图,构造剩余图的大致思路为,若 u → v 运了 f 吨货物,且容量限制为 C(u, v),则剩余图中 u 到 v 的正向弧 u → v 权值为 C(u, v) − f,表示最多还可再运 C(u, v) − f 吨货,并构造权值为 f 的反向弧,表示最多可退大小为f 的货物。
例如,下面两个图分别代表网络的流图及剩余图(剩余图中红色的边为“反向边”,即退货边)
Ford-Fulkerson算法过程的示意图如下:
简单来说,就是原始流 + 剩余图中的可行路 = 原始流的一个改进。
二、Pseudo Code
令 p 表示剩余图 Gf 中的一条简单路径,称为增广路径。并定义 bottleneck(p, f) 为路径 p 上的最小容量边。则 Ford-Fulkerson 算法可以描述为:
算法开始,首先将所有边上的流量初始化为零,此时剩余图与原流图相同,然后进入循环,只要在剩余图中存在一条从s(源点)到t(终点)的路径,算法就执行以下步骤:任选一条s-t的路径,确定选择的s-t路径上的bottleneck(即该路径所包含的边的剩余流量的最小值),在该条路径上再流最小的剩余流量(即bottleneck值),最后更新流图及剩余图。直至在剩余图中不存在任何的s到t的路径,终止循环。
三、算法实现
1.实现环境:Windows 8, VS 2010;
2.图的数据结构——邻接矩阵:
本博文对算法的实现过程,采用邻接矩阵存储图信息,邻接矩阵存储图信息的详细过程参阅“图的存储表示——邻接矩阵”;
3.完整程序详见Github,以下记录实现过程的难点:
(1) s-t路径搜索
程序中采用DFS(深度优先)算法搜索剩余图中的s-t路径,实际上是一种递归的思路,不断考察当前节点的邻居节点,看是否能到达终点t,若可以返回true,若不行则返回前一节点并考察前一节点的其他邻居节点。
/************************************************************************/
/* 深度优先遍历(深度优先搜索) */
/************************************************************************/
bool Graph::dfs(int source, bool* visited)
{
// if current node is the destination node (t), then s-t path is found
if(source == nodeNum - 1)
return true;
int destination;
visited[source] = true;
for(destination = 0; destination < nodeNum; destination++) // search node adjacent to source
{
// find adjacent node which has not been visited
if(edge[source][destination]>0 && !visited[destination])
{
// length of current path increase
pathLength += 1;
// add adjacent node to s-t path
path[pathLength-1] = destination;
// go on searching adjacent node
if( !(this -> dfs(destination, visited)) )
{
// if current adjacent node could not reach the final destinated node
// pathLength decrease since it was increased presiously
// continue to investigate the next adjacent node
pathLength -= 1;
continue;
}
else
{
// if current adjacent node could reach the final destinated node
return true;
}
}
else if(destination == nodeNum - 1)
// no adjacent node, means no s-t path
return false;
}//end for
}
(2) 主函数
以前文图示例子作测试,while循环中不断搜索s-t路径,直到剩余图中找不到s-t路径。当存在s-t路径时,如算法所述,取一s-t路径,找bottleneck,更新流与剩余图。
int main()
{
// graph wiht node s, u, v, t
char nodeName[nodeNum] = {'s', 'u', 'v', 't'};
Graph graph = Graph(nodeNum);
graph.setNode(nodeName);
graph.setEdge('s', 'u', 1);
graph.setEdge('s', 'v', 1);
graph.setEdge('u', 'v', 1);
graph.setEdge('u', 't', 1);
graph.setEdge('v', 't', 1);
// Ford-Fulkerson algorithm find maximum flow
while(graph.findPath())
{
graph.printPath();
graph.findBottleneck();
graph.update();
}
// print maximum flow
cout << "max flow: " << graph.getFlow() << endl;
system("pause");
return 0;
}
四、实现结果
打印算法实现的中间结果如下: