一、问题描述
在下图的网络中,有一个源点机器sss有一个汇点机器ttt,边的权值表示电缆的传输速度。我们的目标使求解源点sss到汇点ttt的最大流量。

二、解题思路
贪心算法:
- 看到这个问题;我们最简单的第一反应就是使用DFS的贪心算法;算法步骤:
- 第一步:使用DFS搜索一条从源点sss到汇点ttt的可以增流的路径(即路径中边的容量c(e)>f(e)c(e)>f(e)c(e)>f(e));并记录下该路径上最多能增多少流(即路径上所有边的(c(e)−f(e)c(e)-f(e)c(e)−f(e))的最小值)。
- 第二步:进行增流,即修改路径上所有边的流量值f(e)f(e)f(e)。
- 第三步:重复第一步和第二步,直到找不到可以增流的路径为止。
- 当然了这种贪心算法肯定是错误的,现在我们来看一下这种算法错在哪里;假设我们对上图的网络进行实行这种贪心算法:这样我们得到的最大流为10.但该网络真正的最大流是11,如下图所示:观察一下两幅图的差异,我们可以发现:问题注意出在1→21\rightarrow21→2边上,该边曾流过大,具体的错误是:在对路径s→1→2→ts\rightarrow1\rightarrow2\rightarrow ts→1→2→t进行增流时增流过大。如果我们对路径s→1→2→ts\rightarrow1\rightarrow2\rightarrow ts→1→2→t增流4,对路径s→1→3→ts\rightarrow1\rightarrow3\rightarrow ts→1→3→t增流6,对路径s→2→ts\rightarrow2\rightarrow ts→2→t增流1,就正确了。问题出在了增流的强度上,当时每天路径的曾流强度又是难以控制的。
Ford-Fulkerson算法:
- 为了贪心算法的问题,我们对原图上的每条边增加一条反向的边,该条反向的边的容量c′(e)c'(e)c′(e)就等于该条边的流量f(e)f(e)f(e) .
- 反向边的用途(作者个人的一个理解):悔棋;我们进行贪心算法时每次都对路径做最大的增流,但是这种做法又是不是最优的,所有当贪心算法进行当前步骤时如果发现以前的步骤增流过多时反向边允许我们悔棋,即允许我们对以前的增加的流量进行撤销。
- 经过贪心算法得到的流量的10的最大流,如上图所示,现在我们增加反向边,如下图所示再增流:向边2→12\rightarrow12→1增流就相当于,撤销以前在边1→21\rightarrow21→2增的流量。
- Ford-Fulkerson算法步骤:
- 第一步:对原图的每条边增加一条反向边,原来边的权值为边的c(e)−f(e)c(e)-f(e)c(e)−f(e),反向边的权值f(e)f(e)f(e);初始时f(e)=0f(e)=0f(e)=0
- 第二步:按照贪心算法进行增流。
- Ford-Fulkerson算法复杂度:O(F∣E∣)O(F|E|)O(F∣E∣)其中FFF为最大流。
代码:
#include <iostream>
#include <vector>
#include<cstring>
using namespace std;
#define Max 1002
#define INF 999999
struct edge//边集结构体
{
int to;//弧尾下标
int cap;//边的权值
int rev;//对应反向边的下标
};
vector<edge> G[Max];//图的邻接表
bool used[Max];
void add_adge(int from,int to,int cap)//向图中填加一条边及其反向边
{
G[from].push_back((edge){to,cap,G[to].size()}) ;
G[to].push_back((edge){from,0,G[from].size()-1}) ;
}
//DFS搜索可以增流的路径
int DFS(int v,int t,int f)
{//v:当前点;t:汇点;f:路径能增加的最大流量
if(v==t)
return f;
used[v]=true;
for(int i=0;i<G[v].size();i++)
{
edge& e=G[v][i];
if(!used[e.to]&&e.cap>0)
{
int d=DFS(e.to,t,min(f,e.cap));
if(d>0)
{
e.cap-=d;
G[e.to][e.rev].cap+=d;
return d;//每次DFS只更新一条路径
}
}
}
return 0;
}
int main()
{
int N,M;//节点数和边数
cin>>N>>M;
int from,to,cap;
for(int i=0;i<M;i++)
{
cin>>from>>to>>cap;
add_adge(from,to,cap);
}
int s,t;
cin>>s>>t;
int flow=0;
while(true)
{
memset(used,0,sizeof(used));
int f=DFS(s,t,INF);
if(f==0)
break;
flow+=f;
}
cout<<"最大流量为:"<<flow<<endl;
return 0;
}
测试样例:
5 7
0 1 10
0 2 2
1 2 6
1 3 6
2 4 5
3 2 3
3 4 8
0 4
输出结果:
最大流量为:11