看了几个博客,整理了一下 建议先了解下EK算法:点击查看
Dinic算法的核心在于:
1、对图进行BFS,绘制出层次图,即对每一个节点进行标记,记录其与源点的距离,称为dep深度。
2、对图进行DFS,过程基本同EK算法,但在搜索过程中,只遍历至当前节点深度+1的节点。
3、反复进行1、2操作,直至第1步层次图中汇点dep值为空,即没有路可抵达汇点,表示最大流已求出,即完成任务。
在层次图上DFS求最大流自然很简单,就是不考虑反向边时能得到的最大流,多次增广后,重新计算层次图,发现s和t不连通,就退出。
它的时间复杂度不会很高,相对EK会快很多,最多只会跑n-1次DFS,因为每次DFS之后s到t的路径至少会少一条,也就是最大距离至少会增加1,而每一次跑DFS最多会用nm的时间,每一次最多遍历每一个节点,每一个节点遍历它所链接的所有边,所以最终时间复杂度为O(N^2*M),相对EK(N*M^2)来说会快很多。
实际上,理论数值所对应的数据几乎不会出现,通常并不会想理论值说的这么慢,只会更快。
而且,对于它,还有一个很重要的优化,对于DFS来说,要保存一个“当前所有弧的最小残量”,如果等于0,必然是不能增广的,找到一条路径直接返回它的值即可,不然会出现多路增广不会退出的情况,还需要多加一次再算增广量,必然会很慢。
还有,当前弧优化,对于一次DFS中,可能会重复访问一个节点,虽然可能性比较小,这时候,已经走过的边自然是不需要再走,那么用一个数组记录上一次这个点走了哪些连接它的边,再次访问时不再重复走,那么就会更快。
代码核心讲解:
void add(int u,int v,int w) // 建图
{
e[len].to=v;
e[len].cap=w;
e[len].nex=head[u];
head[u]=len++;
}
int bfs(int s,int t)
{
memset(vis,0,sizeof(vis));
queue<int> q;
vis[s]=1;
deep[s]=0;
deep[t]=-1;
q.push(s);
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=head[now];i!=-1;i=e[i].nex)
{
int to=e[i].to;
if(vis[to]||e[i].cap<=0) continue; // 残量为0 或者 已经分配深度了 就在进行操作
deep[to]=deep[now]+1;
vis[to]=1;
q.push(to);
}
}
if(deep[t]==-1) return 0; // 当不能到达汇点时 说明也就不存在增广路了
return 1;
}
int dfs(int u,int t,int flow)
{
if(u==t||flow==0) return flow; // 若已达到汇点 或者 当前流量为0 返回
int res=0,f;
for(int i=head[u];i!=-1;i=e[i].nex)
{
int to=e[i].to;
if(deep[to]==deep[u]+1) // 首先他是增广路才继续
{
f=dfs(to,t,min(flow,e[i].cap));//
if(f<=0) continue; // 流量小于等于0 也就不进行了
e[i].cap-=f; // 正减 反加
e[i^1].cap+=f;
flow-=f;
res+=f;
if(!flow) break;
}
}
return res; // 返回此过程流量
}
int Dinic(int s,int t)
{
int ans=0; // 记录最大流
while(bfs(s,t))
{
ans+=dfs(s,t,INF);
}
return ans;
}
是不是想看个题 : poj1273 点击查看