一,概念
1)流网络:简单有向图,且有两个特别的顶点(源点s,汇点t)
2)流的边标识为f(u,v)/c(u,v),流量/容量
3)流的三个性质:1>容量限制 对于所有边 流量<容量
2>反对称性 f(u,v)=-f(v,u)
3>流守恒性 正向流与反响流之和为零
4)割:流网络G=(V,E)的割(S,T)将顶点V划分为S和T=V-S两部分,定义割的容量为C(S)割这条线上S中顶点到T中顶点的容量之和
5)残留网络: 残留容量 c f (u, v) = c(u, v) - f (u, v) //边的容量减去边的实际流量
6)增广路径:对于残留网络 G f 中的一条 s-t 路径 p 称其为增广路径
二,最大流和最小割问题
1)最大流:对于一个流网络 G (V , E ) ,其流量 f 的最大值称为最大流,最大流问题就
是求一个流网络的最大流。
2)最小割:是指流网络中容量最小的割
三、最大流算法的应用
1)最大流模型
例题:现在有 N 只奶牛,F 种食物和 D 种饮料,每只奶牛喜欢其中的一些食物和饮料。现在每种食物和饮料只能分给一只奶牛,每只奶牛也只能吃一种食物和一种饮料,问最多能使多少奶牛既吃到食物又喝到饮料。
解决:由于有 N 只奶牛、F 种食物和 D 种饮料,因此我们可以将这些东西抽象成图中的点。为了方便,我们将食物放在左边,奶牛放在中间,饮料放在右边。由于食物和饮料的使用限制,我们从源点向每种食物连一条边,从每种饮料向汇点连一条边,容量都为 1。而每只奶牛都有喜欢的食物和饮料,因此将每只奶牛喜欢的食物连到这只奶牛,从这只奶牛连到每种它喜欢的饮料。但这样是否就对了呢?实际还是有问题的,因为经过每只奶牛的食物可能超过一种,这就意味着每只奶牛可能会吃超过一组的食物和饮料,而这在题目中是不允许的。怎么解决这个问题呢?我们又回到了流的基本性质:容量限制f (u, v) - c(u, v) 。因此我们将每只奶牛拆成两个点,同一只奶牛的两个点之间连边,容量为 1。这样们就能保证通过每只奶牛的流量为 1 了。每个流对应每种方案,最大流即为最佳方案。
求解网络流的基本思想就是Ford-Fulkerson方法(2F方法):
1. 每次寻找增广路径(就是源点到汇点的一条可行路)。
2. flow += 增广路径能流过的流量
3. 更新剩余网络
4. 然后再做增广路,直到做不出增广路;
Ford-Fulkerson方法是一种迭代的方法。开始时,对所有的u,v∈V有f(u,v)=0,即初始状态时流的值为0。在每次迭代中,可通过寻找一条“增广路径”来增加流值。增广路径可以看成是从源点s到汇点t之间的一条路径,沿该路径可以压入更多的流,从而增加流的值。反复进行这一过程,直至增广路径都被找出来,根据最大流最小割定理,当不包含增广路径时,f是G中的一个最大流。在算法导论中给出的Ford-Fulkerson的伪代码如下:
FORD-FULKERSON(G, s, t)
1 for each edge (u, v) ∈ E[G]
2 do f[u, v] ← 0
3 f[v, u] ← 0
4 while there exists a path p from s to t in the residual network Gf
5 do cf(p) ← min {cf(u, v) : (u, v) is in p}
6 for each edge (u, v) in p
7 do f[u, v] ← f[u, v] + cf(p)
8 f[v, u] ← -f[u, v]
整个流程如图所示:
上面代码中关键的一点就是第四行寻找一条增广路径,如果选择的方法不好,就有可能每次增加的流非常少,而算法运行时间非常长,甚至无法终止。对增广路径的寻找方法的不同形成了求最大流的不同算法,这也是Ford-Fulkerson被称之为“方法”而不是“算法”的原因。而Edmonds_Karp算法使用广度优先搜索来找到一条增广路径,不过这里并不严格要求遍历所有顶点,遇到了终点即可终止遍历。
c++代码:
图:
完整头文件和源码:
//Fulkerson.h header file
//Standard Ford-Fulkerson Algorithm
//Author: Fengdong
#include <stack>
#include <list>
using namespace std;
typedef pair<int,int> flowPair;
typedef struct _node
{
int number;
int C;
int F;
struct _node* next;
_node(int _number,int _C)
{
number = _number;
C = _C;
F = 0;
next = NULL;
}
}Node;
typedef struct _headNode
{
Node* next;
_headNode()
{
next = NULL;
}
}headNode;
class FordFulkerson
{
public:
FordFulkerson();
~FordFulkerson();
void getMaxFlow();
void displayInfo();
private:
void mark();
void init();
void constructAdjList(); //construct adjacent table
void pop(int number); // pop stack
void adjustFlow(); // adjust flow if target node marked
Node* findNode(Node* _current); //which node should be pushed into the stack
Node* getAdjustedNode(int number,int _target); //change F value when adjust flow
private:
static const int headCount = 7;
flowPair currentState[headCount];
int Qs; // how many flow increased
int augmentingPath ; // how many "augmenting path"
headNode nodes[headCount];
stack<Node*>* nodeStack;
int current;
int source ;
int target;
};
/**/
//Fulkerson.cpp source file
//Standard Ford-Fulkerson Algorithm
//Author: Fengdong
#include "stdafx.h"
#include "Fulkerson.h"
FordFulkerson::FordFulkerson()
{
init();
}
FordFulkerson::~FordFulkerson()
{
Node* pre;
Node* suc;
for( int i=0;i<headCount;i++)
{
pre = nodes[i].next;
while(pre)
{
suc = pre->next;
delete pre;
pre = suc ;
}
}
delete nodeStack;
}
void FordFulkerson::init()
{
nodeStack = new stack<Node*>();
constructAdjList();
for( int i=0; i<headCount; i++)
{
currentState[i] = flowPair(i,-1);
}
Qs = 0;
augmentingPath = 0;
current = -1;
source = 0;
target = 6;
nodeStack->push(nodes[0].next);
currentState[0] = flowPair(0,999999);
}
void FordFulkerson::constructAdjList()
{
Node* tmp ;
nodes[0].next = new Node(0,0);
tmp = nodes[0].next ;
tmp->next = new Node(1,13);
tmp = tmp->next ;
tmp->next = new Node(2,9);
nodes[1].next = new Node(3,6);
tmp = nodes[1].next;
tmp->next = new Node(4,5);
nodes[2].next = new Node(1,4);
tmp = nodes[2].next;
tmp->next = new Node(3,5);
tmp = tmp->next ;
tmp->next = new Node(5,5);
nodes[3].next = new Node(4,6);
tmp = nodes[3].next;
tmp->next = new Node(5,4);
tmp = tmp->next;
tmp->next = new Node(6,4);
nodes[4].next = new Node(6,9);
nodes[5].next = new Node(6,10);
}
void FordFulkerson::displayInfo()
{
printf("找到增广路径:%d 个\n",augmentingPath);
printf("共调整流量:%d\n",Qs);
printf("最大流量:");
int sum = 0;
for(Node* node = nodes[0].next; node != NULL;node = node->next)
sum += node->F ;
printf("%d\n",sum);
printf("\n\n Flow after Adjusted \n\n");
for (int i=0;i<headCount;i++)
{
while(nodes[i].next)
{
printf("v%d->v%d : ",i,nodes[i].next->number);
printf("C= %d F= %d\n",nodes[i].next->C,nodes[i].next->F);
nodes[i].next = nodes[i].next->next;
}
printf("\n");
}
}
void FordFulkerson::getMaxFlow()
{
mark();
}
void FordFulkerson::pop(int number)
{
nodeStack->pop();
currentState[number].second = -1;
}
Node* FordFulkerson::findNode(Node* _current)
{
Node* node = nodes[_current->number].next; //head node
Node* result;
if (current != -1)
{
while ((current != target) && (node->number != current))
{
node = node->next;
}
node = node->next;
}
while(node)
{
if (node->C != node->F)
{
result = node;
break;
}
node = node->next;
}
// Target node is the high priority child. :)
while(node)
{
if ((node->number == target) && (node->C != node->F))
{
result = node;
break;
}
node = node->next;
}
return result;
}
Node* FordFulkerson::getAdjustedNode(int number,int _target)
{
Node* node = nodes[number].next;
while(node)
{
if (node->number == _target)
break;
node = node->next;
}
return node;
}
//Adjust Flow if I get a augmenting path
void FordFulkerson::adjustFlow()
{
int Q ;
int index;
int fullFlow = -1;
Node* tmp;
Q = currentState[target].second ;
Qs += Q;
augmentingPath += 1;
index = target;
while ( index > 0)
{
tmp = getAdjustedNode(currentState[index].first,index);
tmp->F += Q ;
if (index != target)
{
currentState[index].second = currentState[index].second - currentState[target].second ;
}
if (tmp->F == tmp->C)
{
fullFlow = index ;
}
index = currentState[index].first;
}
// a forefather is a full node, so there is no flow to his/her children
if (fullFlow != -1)
{
while (nodeStack->top()->number != fullFlow)
{
pop(nodeStack->top()->number);
}
}
return ;
}
void FordFulkerson::mark()
{
Node* adjNode;
int minQ = 999999;
while (!nodeStack->empty())
{
if (nodeStack->top()->number == target)
{
adjustFlow(); //adjust flow if target node marked
current = nodeStack->top()->number;
pop(current);
continue;
}
//there is a full node, pop stack
if ((nodeStack->top()->C == nodeStack->top()->F ) && (nodeStack->top()->number != 0))
{
current = nodeStack->top()->number;
pop(current);
continue;
}
adjNode = findNode(nodeStack->top());
if ( adjNode == NULL)
{
current = nodeStack->top()->number;
pop(current);
continue;
}
else
{
if (nodeStack->top()->number != 0)
{
minQ = currentState[nodeStack->top()->number].second;
}
else
minQ = 999999;
//Mark node
currentState[adjNode->number].first = nodeStack->top()->number;
currentState[adjNode->number].second = adjNode->C - adjNode->F < minQ ? adjNode->C - adjNode->F :minQ;
nodeStack->push(adjNode);
current = -1;
}
}//while
}
int main()
{
FordFulkerson* ffk = new FordFulkerson();
ffk->getMaxFlow();
ffk->displayInfo();
delete ffk;
return 0;
}
运行结果: