Dinic算法是一种解决网络流问题的增光路算法,它先对残量网络建立层次图,然后在层次图上寻找增广路,实现了O(n2m)的时间内求出网络最大流。
算法:
- 遍历残量网络,建立层次图
- 在层次图上寻找增广路进行增光,并将答案加上增广流量
- 重复直至层次图不存在增广路,回到第一点重新建立层次图
- 直到层次图无法建立,即当前流量即为最大流量
无法建立层次图时,说明源点到汇点的容易一条简单路径中,都至少有一条边满载。
当前弧优化
该优化基于一个显而易见的事实,每次建立层次图后,如果在某一次增广前,某个点有一条边增广过了,则这条边在当前的层次图中不会再用到了,即下一次 DFS 这个点的时候直接可以从这条边的下一条边开始。
实现:
struct Node {
struct Edge *firstEdge, *currentEdge;
int level;
} N[MAXN];
struct Edge {
Node *from, *to;
int capacity, flow;
Edge *next, *reversedEdge;
Edge(Node *from, Node *to, int capacity) : from(from), to(to), capacity(capacity), next(from->firstEdge), flow(0) {}
};
struct Dinic {
bool makeLevelGraph(Node *s, Node *t, int n) {
for (int i = 0; i < n; i++) {
N[i].level = 0;
N[i].currentEdge = N[i].firstEdge;
}
std::queue<Node *> q;
q.push(s);
s->level = 1;
while (!q.empty()) {
Node *v = q.front();
q.pop();
for (Edge *e = v->firstEdge; e; e = e->next) {
if (e->flow < e->capacity && e->to->level == 0) {
e->to->level = v->level + 1;
if (e->to == t) return true;
else q.push(e->to);
}
}
}
return false;
}
int findPath(Node *s, Node *t, int limit = INT_MAX) {
if (s == t) return limit;
for (Edge *&e = s->currentEdge; e; e = e->next) {
if (e->flow < e->capacity && e->to->level == s->level + 1) {
int flow = findPath(e->to, t, std::min(limit, e->capacity - e->flow));
if (flow > 0) {
e->flow += flow;
e->reversedEdge->flow -= flow;
return flow;
}
}
}
return 0;
}
int operator()(int s, int t, int n) {
int ans = 0;
while (makeLevelGraph(&N[s], &N[t], n)) {
int flow;
while ((flow = findPath(&N[s], &N[t])) > 0) ans += flow;
}
return ans;
}
} dinic;
inline void addEdge(int from, int to, int capacity) {
N[from].firstEdge = new Edge(&N[from], &N[to], capacity);
N[to].firstEdge = new Edge(&N[to], &N[from], 0);
N[from].firstEdge->reversedEdge = N[to].firstEdge, N[to].firstEdge->reversedEdge = N[from].firstEdge;
}