匹配问题终于可以告一段落,虽然还不是很通透,但时间不允许了。
接下来我们来看看网络流的问题,这个东西和匹配还是有一点相似的,但是也好难。
第一天我们就来讲讲最大流 和 ek算法。。(虽然这个算法在竞赛中基本不用)
- 最大流问题
我们的网络流从最大流开始。那什么是最大流呢?
如上图所示,假设我们现在要把一些物品从结点s(称为源点)运送到结点t(称为汇点),可以从其他结点中转,问能够到达t点的物品的最大值。
这样的问题就被叫做最大流问题,对于一条边(u,v)[u为起点,v为终点],他的物品上限称为容量,记为c(u,v)(对于不存在的边(u,v),c(u,v) = 0);实际运送的物品我们称为流量,记为f(u,v)。在这里的流量是一个总称,即把所有的流进流出都已经加在了一起。即将3个物品从u运送到了v,又把5个物品从v运送到了u,这就等价于我们把两个物品从v运送到了u,所以将某一个结果分成多个情况的相加是没有意义的。这样,我们就可以规定f(u,v)和f(v,u)最多只有一个是整数,也可以都是0。并且f(u,v) = - f(v,u)。
问题的目标
我们的目标是把尽可能多的物品从s运送到t,而其他结点都只是中转。因此对于除了s和t意外的任意节点u,他的流入始终是等于流出的。从s中运送出来的物品数量得那个鱼到达t的物品数量,而这正是此处最大化的目标。即求出maxflow
而在这样的问题中,容量c和流量f满足了3个性质。
1.容量限制 -----> f(u,v) <= c(u,v)
2.斜对称性 -----> f(u,v) = - f(v,u)
3.流量平衡 -----> 指的就是对于除了结点s和t外的其余结点,流入等于流出。
我们也可以说,当一个图满足了这三个性质,我们把它当做一个网络流的模型来对待。
算法:增广路算法
我们从零流(所有边的流量均为0)开始不断增加流量,但在这个同时,我们让每一个点都满足以上三个性质,直到增加到一个最大的流,我们再也不能增加流除非打破这三个性质为止。
这就是增广路算法的基础思想,在这个思想上加上BFS, 我们就得到了Edmonds-Karp算法。
Edmonds-Karp算法
我们先来叙述几个概念。
容量 :上面讲过了。
流量: 即实际上你给这条边流的量
残量: 容量-流量。
增广:求出该道路中所有残量的最小值d,把对应的所有边上的流量减去d。
当且仅当残量网络中不存在增广路,那么此时的流就是最大流。
下面给出ek最简单粗暴的代码,而且这里的存图用的是二维数组的形式。当然这种形式在很多时候就会爆栈,但只要理解了这个,用其他的存图方式也不难解决。
这里再给出一个模板题,用二维数组一定爆栈,但是用其他存图方式就能过
最大流模板题
int N, M ,S ,T;//N个点,M条边,S是起点位置,T是终点位置
int a, b, c;//a为u, b为v, c为c(u,v)
int w[len][len], flow[len][len];w为总图,flow为流量图
int pre[len], res[len], maxflow ;//pre数组记录每一个点的前驱,返回时用 res数组表示余量
int main(){
memset(w, 0, sizeof(w));
cin >> N >> M >> S >> T;
for(int i=1; i<=M; i++){
cin >> a >> b >> c;
w[a][b] +=c;
}
int ans = Edmonds_Karp(S,T);
cout << ans << endl;
return 0;
}
int Edmonds_Karp(int start, int End){
maxflow = 0;
memset(flow, 0, sizeof(flow));
memset(pre, 0, sizeof(pre));
queue <int> q;
while (true){
memset(res, 0, sizeof(res));
res[start] = INF;//第一个点我们给上无穷大的货,这样他就能运出他最多的量了。
q.push(start);
while(!q.empty()){
int h = q.front();q.pop();
for (int i=1; i<=N; i++){
if (!res[i] && w[h][i]>flow[h][i]){//如果余量不是0,就说明这个点我们流过了。一条路一条路找
res[i] = min(res[h], w[h][i] - flow[h][i]);
pre[i] = h;
q.push(i);
}
}
}
if (!res[End]) return maxflow;//若果最后一个点没有余量了,那就说明已经没有留下的了。
for (int i=End; i!=start; i=pre[i]){
flow[pre[i]][i] += res[End];
flow[i][pre[i]] -= res[End];
}
maxflow += res[End];
}
}
这里才给出一个厉害的博主写的厉害的博客
EK算法详解