网络流-EK求最大流
2021.8.17
网络流最大流解决什么问题?
网络流(network-flows)是一种类比水流的解决问题方法(摘自百度百科),最大流是求源点到汇点最大流量的方法。
算法原理
已知起点到终点,很容易想到深搜\广搜求最大值的方式,但是,在最大流问题中,会出现如下情况:
假设1为源点,4为汇点,若对其进行搜索,路径可能有1->2->3->4,随后标记数组有如下标记:
那么,此时,流经1-2线路的水流,汇入了3号节点最终流向4号节点,此时3-4路径已经被灌满,2-4路径没有水流流入,最终汇入汇点的水流量为1。
但是,对于上述情况,最大流应该是如下情形:
此时汇点流入流量为2.
那么,在建图顺序位置时,如何计算出最大的流量?
退回流量,构建增广路径,当流量汇入其他渠道并且有部分无法汇入汇点,那么就代表其应该回到原渠道,按照原渠道走,但是搜索起来肯定难以实现,因此需要建立一条相反的路径,让流过去的水流能有机会返回原来的渠道,并且最多返回流过去的水流。(不管水流走在哪里,只要流入汇点即可,不必求出其准确的路径)如下图:
如此,在1-3路径中的水流流入3号节点时,可以通过2-3路径流回2号节点,再从2-4路径流入4号汇点,此时便可得最大流量为2.(若1-3为2,则也只能退回1).
但是通常情况下,退流一次并不会得到最大流,因此需要多次搜索多次退流。在EK求最大流中,每次选择在部分流量汇入汇点后,对路径的可行流进行更改,构建增广路径。此时图中每个点的残留量不影响结果,因再次搜索时只计算从源点流出的流量。
对其搜索时,正常进行搜索即可:
bool bfs() {
queue<int>q;
memset(vis, 0, sizeof vis);
q.emplace(S); vis[S] = true, d[S] = 0x3f3f3f3f;
while (!q.empty()) {
int t = q.front(); q.pop();
for (int i = head[t]; !i; i = nexte[i]) {
int k = to[i];
if (!vis[k] && wei[i]) {
vis[k] = 1;
d[k] = min(d[t], wei[i]);
pre[k] = i;
if (k == T)return true;
q.emplace(k);
}
}
}
return false;
}
其中,d数组记录路径最多流过的流量,pre数组记录路径编号,当成功汇入汇点,则跳出。
每次对路径进行更新时,则对上一步搜索到的路径进行退流:
int EK() {
int res = 0;
while (bfs()) {//搜索直到没有可行流
res += d[T];
for (int i = T; i != S; i = to[pre[i] ^ 1])//建图时,反向边编号比正向边多1
wei[pre[i]] -= d[T], wei[pre[i] ^ 1] += d[T];//构建增广路径
}
return res;
}
最终返回的res即为最大流。
注:正向路径同样要减去相应的流量大小,因路径已经被部分或全部占用。
模板代码
代码
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string>
#include<string.h>
#include<queue>
using namespace std;
int nexte[20005], wei[20005], to[20005], head[20005];
int n, m, S, T;
int idx;
int pre[20005], d[20005];
bool vis[20005];
void addedge(int u, int v, int c) {
to[idx] = v, wei[idx] = c, nexte[idx] = head[u], head[u] = idx++;
to[idx] = u, wei[idx] = 0, nexte[idx] = head[v], head[v] = idx++;
}
bool bfs() {
queue<int>q;
memset(vis, 0, sizeof vis);
q.emplace(S); vis[S] = true, d[S] = 0x3f3f3f3f;
while (!q.empty()) {
int t = q.front(); q.pop();
for (int i = head[t]; !i; i = nexte[i]) {
int k = to[i];
if (!vis[k] && wei[i]) {
d[k] = min(d[t], wei[i]), pre[k] = i;
if (k == T)return true;
q.emplace(k);
vis[k] = 1;
}
}
}
return false;
}
int EK() {
int res = 0;
while (bfs()) {
res += d[T];
for (int i = T; i != S; i = to[pre[i] ^ 1])
wei[pre[i]] -= d[T], wei[pre[i] ^ 1] += d[T];
}
return res;
}
int main() {
cin >> n >> m >> S >> T;
memset(head, -1, sizeof head);
for (int i = 0; i < m; i++){
int a, b, c;
cin >> a >> b >> c;
addedge(a, b, c);
}
cout << EK() << endl;
return 0;
}