最大流-dinic算法

dinic与EK算法的比较:

1、使用了层次网络,减小了寻找增广路的代价。

2、允许一次dfs多次增广,一次dfs可以把本层级网络的所有增广路都找出来。

学习EK算法请转到 最大流-EK(Edmond—Karp)算法


算法步骤:

(1)构建残余网络。

(2)构建层次网络。即初始化距离标号level。距离标号是指定点距离源点边数的最小值。

(3)dfs多路增广。这里找到一条增广路之后,一直回退直到一个顶点v扣除增广流量后还有残余流量,沿途更新边的 残余容量。注意到第一次返回到v时,v之前的边的容量并没有更新。直到从v点出发的所有增广路都找完了,才会向前返回并更新边的容量,这时更新值为从v点出发的所有增广路的总流量。相当于将流量积攒下来一起扣除。

(4)重复步骤(2)(3)。


第二次看自己写的博客,竟然没有看懂,于是就加深了一次对dinic的理解。

一直纠结于既然dinic可以一次dfs多次增广,为何还要多次建层次网络。后来画了一个图就明白了。


第一次建层次网络,T的距离标号为2,只能走S->1->T这条增广路。

第二次建层次网络时会将第一次获得的增广路的饱和边删去,于是就只能走S->2->3->4->T这条增广路,T的距离标号为4。

推荐一个大佬的博客:http://blog.youkuaiyun.com/wall_f/article/details/8207595#comments


题目链接:Drainage Ditches

自己整了一份比较简洁的模板

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
#define inf 1<<29
int n,m;
int G[205][205];//残余网络
int level[205];//层级网络,各点的层级
int bfs(int s,int t)//构建层级网络
{
        memset(level,0,sizeof level);
        level[s]=1;
        queue<int> q;
        q.push(s);
        while(!q.empty())
        {
                int top=q.front(); q.pop();
                for(int i=1;i<=n;i++)
                if(!level[i] && G[top][i])//如果这个点没有被搜过,并且top->i这条边的残余容量大于0
                {
                        level[i]=level[top]+1;//将这个点的层级赋为上个点层级+1
                        q.push(i);//将这个点入队
                }
        }
        return level[t];//如果本层级网络能够到达终点
}
int dfs(int v,int t,int flow)//v为当前结点,t为终点,flow为v点存留的流量
{
        int s=flow,increase;//s记录源点的流量,也就是INF,increase为本条增广路的流量
        if(v==t) return flow;//如果搜到了终点,则返回flow,此时flow=increase
        for(int i=1;i<=n;i++)
        {
                if(G[v][i] && level[i]==level[v]+1)//如果v->i这条边残余容量>0,并且i为v的下一个层级
                {
                        increase=dfs(i,t,min(flow,G[v][i]));
                        G[v][i]-=increase;//更新残余网络
                        G[i][v]+=increase;
                        flow-=increase;//v点的残余容量减去增广消耗的流量
                }
        }
        return s-flow;//源点原容量(inf)-源点剩余容量(flow)=本层次网络增广的总流量
}
int dinic(int s,int t)
{
        int maxflow=0,f;
        while(bfs(s,t)) maxflow+=dfs(s,t,inf);//每次尝试构建层次网络,如果构建成功,maxflow加上本网络增广总流量
        return maxflow;
}
int main()
{
        while(~scanf("%d%d",&m,&n))
        {
                memset(G,0,sizeof G);
                int ui,vi,wi;
                for(int i=0;i<m;i++)
                {
                        scanf("%d%d%d",&ui,&vi,&wi);
                        G[ui][vi]+=wi;
                }
                printf("%d\n",dinic(1,n));
        }
        return 0;
}


### DINIC算法解决最大流问题 #### 原理概述 DINIC算法是一种用于计算网络流中的最大流的有效算法。该算法通过构建层次图并在此基础上查找增广路径来逐步增加流量,直到无法再找到新的增广路径为止[^1]。 #### 层次图的建立 为了提高效率,DINIC算法采用了一种称为“层次图”的结构。具体来说,就是利用宽度优先搜索(BFS)从源节点出发给每一个可达结点分配一个距离标签,表示到达这个顶点至少要经过几条边。只有当存在一条容量大于0且终点未被访问过的有向边时才会继续向下一层扩展[^2]。 #### 寻找增广路径 一旦建立了层次图之后,就可通过深度优先搜索(DFS)尝试沿着这些已标记好的层级关系去探寻可能存在的增广路径。这里需要注意的是,在一次完整的DFS过程中可以一次性找出多条独立互不干扰的最短长度增广路,并据此调整实际流动量[^4]。 #### 当前弧优化技术 普通版本下的DINIC可能会遇到性能瓶颈,尤其是在处理大规模稀疏图的情况下。为此引入了所谓的“当前弧”机制作为改进措施之一。“当前弧”指的是对于每个节点维护其尚未完全探索完毕的第一条有效出边位置指针;每当完成一轮针对某特定起点v的所有潜在目标w之间的遍历时,则更新此指向下一可用候选者处,从而减少不必要的重复扫描操作次数,显著提升整体执行速度[^3]。 ```cpp struct Edge { int to, cap, flow; }; vector<Edge> edges; vector<int> adj[MAX_N]; int level[MAX_N], cur[MAX_N]; void addEdge(int u, int v, int c) { edges.push_back({v, c, 0}); edges.push_back({u, 0, 0}); // 反向边初始化为零流量 adj[u].push_back(edges.size() - 2); adj[v].push_back(edges.size() - 1); } bool bfs(int s, int t) { /* ... */ } int dfs(int node, int sink, int flow) { /* ... */ } // 主函数调用部分省略... while (bfs(sink)) { memset(cur, 0, sizeof(cur)); while (int f = dfs(source, sink, INT_MAX)) max_flow += f; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值