先推荐一个讲网络流的博客,我的网络流知识均吸收于此 传送门
EdmondsKarp算法基本思想:从起点到终点进行bfs,只要存在路,说明存在增广路径,则取这部分路 权值最小的一部分,即为增广路径(也就是这一部分路的最大流量)。然后将这条路上的正向权值都减去min,反向权值都加上min(即,m[i][j]-min,m[j][i]+min,为什么等会再解释)。然后重复此操作,最终就得到了最大流。
先上模板(也是取自于刚才的博客,真的写的很精简很好懂)。
邻接矩阵版本。
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MAXN = 300;
const int MAX_INT = ((1 << 31) - 1);
int n; // 图中点的数目
int pre[MAXN]; // 从 s - t 中的一个可行流中, 节点 i 的前序节点为 Pre[i];
bool vis[MAXN]; // 标记一个点是否被访问过
int mp[MAXN][MAXN]; // 记录图信息
bool bfs(int s, int t){
queue <int> que;
memset(vis, 0, sizeof(vis));
memset(pre, -1, sizeof(pre));
pre[s] = s;
vis[s] = true;
que.push(s);
while(!que.empty()){
int u = que.front();
que.pop();
for(int i = 1; i <= n; i++){
if(mp[u][i] && !vis[i]){
pre[i] = u;
vis[i] = true;
if(i == t) return true;
que.push(i);
}
}
}
return false;
}
int EK(int s, int t){
int ans = 0;
while(bfs(s, t)){
int mi = MAX_INT;
for(int i = t; i != s; i = pre[i]){
mi = min(mi, mp[pre[i]][i]);
}
for(int i = t; i != s; i = pre[i]){
mp[pre[i]][i] -= mi;
mp[i][pre[i]] += mi;
}
ans += mi;
}
return ans;
}
这部分代码唯一不好理解的就是,
for(int i = t; i != s; i = pre[i]){
mp[pre[i]][i] -= mi;
mp[i][pre[i]] += mi;
}
mp[pre[i]][i]-mi好理解,因为已经流过了嘛,那为什么有mp[i][pre[i]]+=mi呢?让我们看一个具体的例子。
按照我们的做法,第一次得到了1-2-4-6这条路径,会发现最大流此时是2,然后将路径上的值全部减去2,然后得到的图是这样的:
如果只减掉了正向的路径的权值,这个图就不连通了,也就是答案是2,然而其实答案是3,为什么呢,让我们加上反向的权值看一下:
红色的就是反向的权值,然后再进行上述过程,会发现还有一条 1-3-4-2-5-6的路径,权值为1,所以答案是2+1=3.(这个答案大家可以根据第一幅图自己想一下,会发现就是3,好神奇有木有?)
那为什么这么神奇呢,我的理解是,反向路径代表了,这条路曾经通了多少水,比如2-4这条就通2滴水,然后在第二条路径中通过的这1滴水,其实就是2号路径跟1号路径说,你原本在2-4这条路上有两滴水,现在分一滴到我想去的地方,然后我的水去你想去的地方,这样我们都能实现目标。(想一下是不是这么回事)。
解决了这个难题,那EK算法的模板你就理解啦!
接下来送上邻接表的版本。
const int MAXN = 430;
const int MAX_INT = (1 << 30);
struct Edge{
int v, nxt, w;
};
struct Node{
int v, id;
};
int n, m, ecnt;
bool vis[MAXN];
int head[MAXN];
Node pre[MAXN];
Edge edge[MAXN];
void init(){
ecnt = 0;
memset(edge, 0, sizeof(edge));
memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w){
edge[ecnt].v = v;
edge[ecnt].w = w;
edge[ecnt].nxt = head[u];
head[u] = ecnt++;
}
bool bfs(int s, int t){
queue <int> que;
memset(vis, 0, sizeof(vis));
memset(pre, -1, sizeof(pre));
pre[s].v = s;
vis[s] = true;
que.push(s);
while(!que.empty()){
int u = que.front();
que.pop();
for(int i = head[u]; i + 1; i = edge[i].nxt){
int v = edge[i].v;
if(!vis[v] && edge[i].w){
pre[v].v = u;
pre[v].id = i;
vis[v] = true;
if(v == t) return true;
que.push(v);
}
}
}
return false;
}
int EK(int s, int t){
int ans = 0;
while(bfs(s, t)){
int mi = MAX_INT;
for(int i = t; i != s; i = pre[i].v){
mi = min(mi, edge[pre[i].id].w);
}
for(int i = t; i != s; i = pre[i].v){
edge[pre[i].id].w -= mi;
edge[pre[i].id ^ 1].w += mi;
}
ans += mi;
}
return ans;
}
// 加边
addEdge(u, v, w);
addEdge(v, u, 0);
// 调用
int ans = EK(s, t);