求最大流的算法有很多种,EK算法就是其中较简单的一种,这个算法没有进行太多的优化,时间复杂度是O(nm^2)。
这个算法的核心就是不断去维护残量图。先对原图建一个残量图,初始残量图就是每条边都加一条反向的0权边,之后进行若干次bfs,每次bfs都去寻找一条从起点到终点的通路,找到以后通路上的各边权减去最小边权,同时反向边加上最小边权,这可能使得某些边的权为0,当边权为0说明这条路已经不能再有流量了,也就是条断边。随着bfs次数增多,这个图最终会变得不再连通,此时就得到了答案了,答案就是之前每次找到的最小边权之和。
网络流算法运行时间比较玄学,虽然这个算法的时间复杂度为O(nm^2),但对于n+m处于小于1e4的题目都是可以解决的。
以2171. EK求最大流 为例放一个模板:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#include <queue>
using namespace std;
int n, m, s, t, head[1005], cnt, _min[1005], pre[1005];
bool vis[1005];
struct edge
{
int to, next, w;
}e[20005];
void add(int u, int v, int w)
{
e[cnt].to = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt;
cnt++;
}
bool bfs()
{
memset(vis, false, sizeof vis);
vis[s] = true;
_min[s] = 0x3f3f3f3f;
queue<int> q;
q.push(s);
while(q.size())
{
int now = q.front();
q.pop();
if(now == t) return true;
for(int i = head[now]; ~i; i = e[i].next)
{
int to = e[i].to;
if(e[i].w && !vis[to])
{
vis[to] = true;
_min[to] = min(e[i].w, _min[now]);
pre[to] = i;//这里要记录上一条边
q.push(to);
}
}
}
return false;
}
int EK()
{
int ans = 0;
while(bfs())//bfs判连通
{
ans += _min[t];
for(int i = t; i != s; i = e[pre[i]^1].to)
e[pre[i]].w -= _min[t], e[pre[i]^1].w += _min[t];
}
return ans;
}
signed main()
{
scanf("%d%d%d%d", &n, &m, &s, &t);
memset(head, -1, sizeof head);
for(int i = 1; i <= m; i++)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
add(u, v, w), add(v, u, 0);
}
cout << EK();
return 0;
}