最大流EK(Edmonds-Karp)
算法思路
每次在残量网络上BFS找增广路进行增广。算法较为简明简单,下面主要讨论时间复杂度。
设有 \(n\) 个点 \(m\) 条边,则每次增广耗时 \(O(n+m)\) 。下证最多增广 \(O(mn)\) 次。
由于使用 \(BFS\) 进行增广,所以每次增广都是从起点到终点的最短路。比较显然的是,每次增广之后所有点到起点的最短路不变小。
定义限制此次增广最大流量的为关键边,于是此次增广后,这条边(设从 \(u\) 到 \(v\) )就会消失。这条边再次出现的条件是这条边的反向边被增广。于是这条边再次成为关键边的必要不充分条件是它的反向边被增广。
考察两个点到起点的距离 \(d[u]\) 和 \(d[v]\) 。这条边被删时是 \(d[v]=d[u]+1\) ,它的反向边被增广时是 \(d[u]=d[v]+1\) 。由于 \(d\) 只变大不变小,所以至少是 \(d[v]\) 变大了 \(2\) 。所以这条边成为关键边的次数至多为 \(O(n)\) 。
每次增广都有一条关键边,所以最多增广 \(O(nm)\) 次。
总时间复杂度为 \(O(nm(n+m))\) ,实际操作时远小于这个值。
题目
参考程序
#include <bits/stdc++.h>
const int Maxn = 110;
const int Maxm = 5010;
int n, m, s, t;
int u, v, c;
namespace graph {
struct edge {
int To, Remain, Next;
edge() {}
edge(int _To, int _Remain, int _Next)
: To(_To), Remain(_Remain), Next(_Next)
{}
};
int Start[Maxn], Space;
edge Edge[Maxm << 1];
inline void Init() {
Space = 0; memset(Start, 255, sizeof(Start));
return;
}
inline void AddEdge(int u, int v, int c) {
Edge[Space++] = edge(v, c, Start[u]); Start[u] = Space - 1;
Edge[Space++] = edge(u, 0, Start[v]); Start[v] = Space - 1;
return;
}
}
long long Edmonds_Karp_EK(int Start, int Destination);
int main() {
scanf("%d%d%d%d", &n, &m, &s, &t);
graph::Init();
for (int i = 1; i <= m; ++i) {
scanf("%d%d%d", &u, &v, &c);
graph::AddEdge(u, v, c);
}
printf("%lld\n", Edmonds_Karp_EK(s, t));
return 0;
}
int Vis[Maxn], From[Maxn], Index[Maxn];
int Head, Tail, Queue[Maxn], Temp;
int BFS(int Start, int Destination) {
int Ans = 0;
memset(Vis, 0, sizeof(Vis));
Head = Tail = 1; Queue[Tail] = Start;
Vis[Start] = INT_MAX;
while (Head <= Tail) {
//printf("%d %d\n", Queue[Head], Vis[Queue[Head]]);
for (int t = graph::Start[Queue[Head]]; t != -1; t = graph::Edge[t].Next) {
//printf(" %d\n", graph::Edge[t].To);
if (graph::Edge[t].Remain <= 0) continue;
if (Vis[graph::Edge[t].To] != 0) continue;
Vis[graph::Edge[t].To] = std::min(Vis[Queue[Head]], graph::Edge[t].Remain);
From[graph::Edge[t].To] = Queue[Head];
Index[graph::Edge[t].To] = t;
Queue[++Tail] = graph::Edge[t].To;
}
++Head;
}
Ans = Vis[Destination];
if (Ans) {
//printf("\n\n\nFind %d\n", Ans);
Temp = Destination;
while (Temp != Start) {
//printf(" %d\n", Temp);
graph::Edge[Index[Temp]].Remain -= Ans;
graph::Edge[Index[Temp] ^ 1].Remain += Ans;
Temp = From[Temp];
}
//printf(" %d\n", Temp);
}
return Ans;
}
long long Edmonds_Karp_EK(int Start, int Destination) {
long long Ans = 0, Temp = 0;
Temp = BFS(Start, Destination);
while (Temp) Ans += Temp, Temp = BFS(Start, Destination);
return Ans;
}