【算法模板】Dinic
以下内容摘自OI Wiki
Dinic算法的过程是这样的:每次增广前,我们先用BFS来将图分层。
设源点的层数为0,那么一个点的层数便是它离源点的最近距离。
通过分层,我们可以干两件事情:
如果不存在到汇点的增广路(即汇点的层数不存在),我们即可停止增广。
确保我们找到的增广路是最短的。(原因见下文)
接下来是 DFS 找增广路的过程。
我们每次找增广路的时候,都只找比当前点层数多1的点进行增广(这样就可以确保我们找到的增广路是最短的)。
Dinic 算法有两个优化:
多路增广:每次找到一条增广路的时候,如果残余流量没有用完怎么办呢?我们可以利用残余部分流量,再找出一条增广路。这样就可以在一次DFS中找出多条增广路,大大提高了算法的效率。
当前弧优化:如果一条边已经被增广过,那么它就没有可能被增广第二次。那么,我们下一次进行增广的时候,就可以不必再走那些已经被增广过的边。
容易知道,每次增广后最短路径的长度都会增加,故最多进行n-1次DFS,而每次DFS的时间不超过O(nm),总的时间复杂度一定小于O(n^2m),实际上远小于这个复杂度。
struct Edge {
int from, to, cap, flow;
};
struct Dinic {
int s, t;
bool vis[N];
int d[N];
int cur[N];
vector<Edge> edges;
vector<int> G[N];
void AddEdge(int from, int to, int cap) {
edges.push_back({from, to, cap, 0});
edges.push_back({to, from, 0, 0);
//edges.push_back({to, from, cap, 0}); 如果是无向图.
G[from].push_back(edges.size() - 2);
G[to].push_back(edges.size() - 1);
}
bool BFS() {
memset(vis, 0, sizeof(vis));
queue<int> Q;
Q.push(s);
d[s] = 0;
vis[s] = 1;
while (!Q.empty()) {
int u = Q.front(); Q.pop();
for (int i = 0; i < G[u].size(); i++) {
Edge& e = edges[G[u][i]];
int v = e.to;
if (!vis[v] && e.cap > e.flow) {
vis[v] = 1;
d[v] = d[u] + 1;
Q.push(v);
}
}
}
return vis[t];
}
int DFS(int u, int a) {
if (u == t || a == 0) return a;
int flow = 0, f;
for (int& i = cur[u]; i < G[u].size(); i++) { //这里取引用,使得u的当前弧被i改变,再次访问到u时,将跳过u已经访问过的支路
Edge& e = edges[G[u][i]], ee = edges[G[u][i] ^ 1];
int v = e.to;
if (d[v] == d[u] + 1 && (f = DFS(v, min(a, e.cap - e.flow))) > 0) {
e.flow += f;
ee.flow -= f;
flow += f;
a -= f;
if (a == 0) break;
}
}
return flow;
}
int Maxflow(int s, int t) {
this->s = s; this->t = t;
int flow = 0;
while (BFS()) {
memset(cur, 0, sizeof(cur));
flow += DFS(s, INF);
}
return flow;
}
};