问题描述:
最大传输量:
网络中有两台计算机s(source)和t(sink),现在想从s传输数据到t,该网络中一共有N台计算机,其中一些计算机之间有一条单向的通信电缆,每条通信电缆都有对应的1s内所能传输的最大的数据量。当其他计算机之间没有数据传输时,在1秒内s最多可以传送多少数据到t?
Ford-Fulkerson算法
把计算机当作顶点,把连接计算机的通信电缆当作边,就可以当作有向图来考虑。 图中的每条边,都有对应的最大可能的数据传输量。这样,就可以把问题转为如下形式。
- 记每条边对应的实际数据传输量为
- 传输量满足
- 数据在传输过程中既不会减少也不会增加,收到的数据量和发出的数据量应该相等
- 目标是最大化从s出发的数据量
首先考虑贪心算法
- 寻找
2. 如果不存在满足条件的路径,则结束。否则,沿该路径尽可能的增加,如此递归。
很容易发现这样得到的结果并非最大值。(不满足贪心选择性)。
而实际的最大值为11:
贪心选择性的矛盾:对于一条路,可能会影响之后路的流,于是在贪心之后可以对撤销,以供选择。于是可将算法进行如下改进。
- 只利用的边或者满足的边对应的反向边,寻找
- 如果不满足这样的路径,则结束。否则,尽可能的增加流,并如此递归
称1中对应的边和E组成的图叫残余网络
称残余网络中的路径为增广路
//Ford-Fulkerson算法领接表实现。
//没有保存f(e)的值,而是直接改变c(e)的值
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
#define MAX_V 1<<10
#define INF 0x3f3f3f3f
#define int long long
//用于边的结构体
typedef struct edge { int to, cap, rev; }edge;
//图的领接表
vector<edge>G[MAX_V];
//DFS访问标记
bool used[MAX_V];
//向图中增加一条从s到t容量为cap的边
void add_edge(int from, int to, int cap) {
edge a = { to,cap,G[to].size() };
edge b = { from,0,G[from].size()};
G[from].push_back(a);
G[to].push_back(b);
}
//通过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[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;
}
}
}
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);
if (f == 0) return flow;
flow += f;
}
}
unsigned main() {
ios::sync_with_stdio(false);
//to do code
}
记最大流为F,Ford-Fulkerson算法最多进行F次深度优先搜索,所以其复杂度为。
不过这是一个很松的上界,达到这样最坏复杂度的情况几乎不存在。
那么这个算法总能求出最大流吗?答案是肯定的,下面根据最小割来说明。
考虑如下问题:对于给定网络,为了保证没有,需要删去的边的总容量的最小值是多少。
考虑
由此可见
考虑通过Ford-Fulkerson算法求得的流.记得对应的残余网络中,因为对应的残局网络中
根据的定义
再由不等式知
由此证明了Ford-Fulkerson算法的正确性,同时得到:
最大流等于最小割
本文借鉴: [日]秋叶拓哉:挑战程序设计