先推荐一个讲网络流的博客,我的网络流知识均吸收于此 传送门
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);