用于计算最大流,带有当前弧优化
下面是对算法的简明概述:
首先dinic算法属于增广路算法,通过不断寻找从源点到汇点的增广路来实现扩流,但想较之Ford-Fulkerson算法来说,dinic在寻找增广路之前,将原图进行分层处理,即 以源点为深度为零的点,不断向下按照深度将原图的点标记;并且永远寻找最短的增广路来进行优化。因为最短的增广路在增广的过程中永远不会变短。
之后是代码实现:
- 首先是边的存储结构 struct Edge
- [1] to 表示边的终点
- [2] cap表示这条边所代表的流量
- [3] rev表示反向边在终点to邻接表中的位置,即用于指向反向边adj[to][rev]
接着是函数的说明
-
addEdge(int star,int finish,int cap)
在图中建立一条边以及其反向边 -
dinic()
dinic算法主体。首先bfs对原图分层,记录每个节点深度,之后dfs寻找图中最短增广路扩流。如果在现有图中,找不到增广路则重新对图分层,直到分层后不能到达汇点,即汇点没有深度标记,结束程序,返回最大流 -
bfs()
从源点开始对原图进行搜索,标记每个点的最小深度(选取不同路径对应不同的深度,在这里选取每个点在搜索过程中第一次出现时,所对应的深度,也就是这个点的对源点的最小深度)。 -
dfs(int star,int finish,int f)
在分层图中寻找最短的增广路,这就要求对于当前节点 star 到下一个节点的深度有如下的要求level[star] = level[to] + 1
当前弧优化为 避免在寻找增广路的过程中对一条已知没用的边反复搜索。
在同一分层图中,可能会重复多次dfs。假设 在一次搜索过程中,到达节点 v ,并选择了边 adj[v][k] 。之后在下一次搜索中,有到达了节点 v ,那么我们可以根据上一次到达这个节点的搜索结果知道,对于任意的 i<k ,边adj[v][i] 都不能使用了,因此我们只需要从 k 开始搜索即可。而当图重新分层后,就需要把这些记录置零,重新记录。
在dfs中的改动
for(int &i=cur[star];i<adj[star].size();i++)
在dinic主体中改动
memset(cur,0,sizeof(cur));
while(d=dfs(starNode,finishNode,INF),d>0)
sum+=d;
完整代码如下:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <string>
#define MAXN
#define starNode 0
#define finishNode
#define INF 0x3f3f3f3f
#define ll long long
#define re return
#define getLen(name,index) name[index].size()
#define mem(a,b) memset(a,b,sizeof(a));
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;
struct Edge{
int to;
int cap;
int rev;
};
int cur[MAXN];
int level[MAXN];
vector<Edge> adj[MAXN];
inline void addEdge(int star,int finish,int cap);
int dinic();
void bfs();
int dfs(int star,int finish,int f);
int main(){
ios::sync_with_stdio(false);
re 0;
}
inline void addEdge(int star,int finish,int cap){
adj[star].push_back((Edge){finish,cap,getLen(adj,finish)});
adj[finish].push_back((Edge){star,0,getLen(adj,star)-1});
}
int dinic(){
int sum=0,d;
while(true){
bfs();
if(level[finishNode]<0)
break;
mem(cur,0);
while(d=dfs(starNode,finishNode,INF),d>0){
sum+=d;
}
}
re sum;
}
void bfs(){
mem(level,-1);
level[starNode]=0;
queue<int> Q;
Q.push(starNode);
while(!Q.empty()){
int now=Q.front();
Q.pop();
int dep=level[now];
rep(i,0,getLen(adj,now)){
Edge &e=adj[now][i];
if(e.cap>0 && level[e.to]<0){
level[e.to]=dep+1;
Q.push(e.to);
}
}
}
}
int dfs(int star,int finish,int f){
if(star==finish)
re f;
for(int &i=cur[star];i<getLen(adj,star);i++){
Edge &e=adj[star][i];
if(e.cap>0 && level[e.to]==level[star]+1){
int d=dfs(e.to,finish,min(f,e.cap));
if(d>0){
e.cap-=d;
adj[e.to][e.rev].cap+=d;
re d;
}
}
}
re -1;
}