最大传输量
网络中有两台计算机s和t,现在想从s传输数据到t。该网络中一共有N台计算机,其中一些计算机之间连有一条单向的通信电缆,每条通信电缆都有对应的1秒钟内所能传输的最大数据量。当其他计算机之间没有数据传输时,在1秒钟内s最多可以传送多少数据到t?
将上述网络当做一个有向图。图中每条边e都有对应的最大可能的数据传输量。这样就可以把问题转为如下形式:
(1)记每条边对应的实际数据传输量为f(e)。
(2)传输量应该满足如下限制:
0<=f(e)<=c(e)
(3)数据在传输过程中既不会增加也不会减少,收到的数据量和发出的数据量应该相等。
目标是:最大化从s发出的数据量。
我们称使得传输量最大的f为最大流,而求解最大流的问题为最大流问题。此外,我们称c为边的容量,f为边的流量,s为源点,t为汇点。
(一)贪心算法
(1)找一条s到t 的只经过f(e)<c(e)的边的路径;
(2)如果不存在满足条件的路径,则结束算法。否则,沿着该路径尽可能地增加,返回第(1)步。
但是得到的结果并非最优的。将最优结果和贪心算法结果的流量求差,得到差值图,可以发现,如果将差值为-1的流给推回去,而得到新的流。
于是将贪心算法做如下改进:
(1)只利用满足f(e)<c(e)的e或者f(e)>0的e对应的反向边rev(e),寻找一条s到t的路径。
(2)如果不存在满足条件的路径,则结束。否则,沿着该路径尽可能地增加流,返回第(1)步。
注:我的理解:可以先按照贪心算法,把结果求出来,然后把结果中流量不为0的边加一条反向边,继续使用算法,看还是否能求出一条新的路径。
残余网络:考虑的f(e)<c(e),和满足f(e)>0的e对应的反向边rev((e)所组成的图
下面是一个Ford-Fulkerson算法的邻接表实现的例子,这里没有保存f(e)的值,取而代之的是直接改变c(e)的值
//用于表示边的结构体(终点,容量,反向边)
struct edge{int to,cap,rev};
vector<edge> G[MAX_V];//图的邻接表表示
bool used[MAX_V];//DFS中用到的访问标记
//向图中增加一条从s到t容量为cap的边
void add_edge(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){
if(v==t) return f;
used[v]=true;
for(int i=0;i<G[v].size();i++){
edge &e=G[v][i];
if(!used[G.to]&&e.cap>0){
int d=dfs(e.to,t,min(f,e.cap));
if (d>0){
e.cap=e.cap-d;
G[e.to][e.rev].cap+=d;
return d;
}
}
}
return 0;
}
//求解从s到t的最大流
int max_flow(int s,int t){
int flow=0;
for(; ;){
memset(used,0,sizeof(used));
int f=dfs(s,t,INF);
int(f==0) return flow;
flow+=f;
}
}