【概述】

Dinic 算法在 EK 算法的基础上进行了优化,其时间复杂度为 O(n*n*m)。

Dinic 在找增广路的时也是找最短增广路, 但与 EK 算法不同的是 Dinic 算法并不是每次 bfs 只找一个增广路,他会首先通过一次 bfs 为所有点添加一个标号,构成一个层次图,然后在层次图中寻找增广路进行更新。

【基本思想】

1.初始化容量网络与网络流

2.构造残量网络,根据残留网络通过 BFS 计算层次网络,若汇点不在层次图中(汇点层次为 -1),则结束

3.在层次网络中使用一次 DFS 进行增广,DFS 执行完毕,该阶段的增广也执行完毕

4.转步骤 2

【求最大流】

1.一般方式

使用邻接矩阵的形式,适用于点较少的情况。

int n,m;//n个点m条边
int S,T;//源点、汇点
int level[N];//存储结点分层层数
struct Edge{int cap;int flow;
}edge[N][N];
bool bfs(){//构造层次网络memset(level,0,sizeof(level));queue<int> Q;Q.push(S);level[S]=1;while(!Q.empty()){int x=Q.front();Q.pop();for(int y=1;y<=n;y++){if(!level[y]&&edge[x][y].cap>edge[x][y].flow){level[y]=level[x]+1;Q.push(y);}}}return level[T]!=0;
}int dfs(int x,int cp){//计算可增加流量if(x==n)return cp;int flow=cp;//记录从x到t的最小残量for(int y=1;y<=n;y++){if(level[x]+1==level[y]){if(edge[x][y].cap>edge[x][y].flow){int minn=min(flow,edge[x][y].cap-edge[x][y].flow);int newFlow=dfs(y,minn);edge[x][y].flow+=newFlow;edge[y][x].flow-=newFlow;flow-=newFlow;}}if(flow==0)break;}return cp-flow;
}
int dinic(){int flow=0;int tf=0;while(bfs()){while(tf=dfs(1,INF)){flow+=tf;}}return flow;
}
int main(){scanf("%d%d",&n,&m);memset(edge,0,sizeof(edge));while(m--){int x,y,w;scanf("%d%d%d",&x,&y,&w);edge[x][y].cap+=w;//便于处理重边}S=1,T=n;printf("%d\n",dinic());return 0;
}

2.vector 邻接表实现

使用 vector 邻接表与结构体结合,适用于点较多的情况,但速度稍慢于邻接矩阵。

struct Edge{int from,to;int cap,flow;Edge(){}Edge(int from,int to,int cap,int flow):from(from),to(to),cap(cap),flow(flow){}};
int n,m;             //结点数,边数(含反向弧)
int S,T;             //源点、汇点
vector<Edge> edges;  //边表,edges[e]和edges[e^1]互为反向弧
vector<int> G[N];    //邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
bool vis[N];         //BFS使用,标记一个节点是否被遍历过
int dis[N];          //dis[i]表从起点s到i点的距离(层次)
int cur[N];          //cur[i]表当前正访问i节点的第cur[i]条弧
void addEdge(int from,int to,int cap){edges.push_back( Edge(from,to,cap,0) );edges.push_back( Edge(to,from,0,0) );int m=edges.size();G[from].push_back(m-2);G[to].push_back(m-1);
}
bool BFS(){//构建层次网络memset(vis,0,sizeof(vis));dis[S]=0;vis[S]=true;queue<int> Q;//用来保存节点编号Q.push(S);while(!Q.empty()){int x=Q.front();Q.pop();for(int y=0;y<G[x].size();y++){Edge& e=edges[G[x][y]];if(!vis[e.to] && e.cap>e.flow){vis[e.to]=true;dis[e.to]=dis[x]+1;Q.push(e.to);}}}return vis[T];
}int DFS(int x,int cp){//cp表示从s到x目前为止所有弧的最小残量if(x==T || cp==0)return cp;int flow=0,newFlow;//flow用来记录从x到t的最小残量for(int &y=cur[x];y<G[x].size();y++){Edge &e=edges[G[x][y]];if(dis[x]+1==dis[e.to]){int minn=min(cp,e.cap-e.flow);newFlow=DFS(e.to,minn);if(newFlow>0){e.flow+=newFlow;edges[G[x][y]^1].flow-=newFlow;flow+=newFlow;cp-=newFlow;if(cp==0)break;}}}return flow;
}
int Dinic(){int flow=0;while(BFS()){memset(cur,0,sizeof(cur));flow+=DFS(S,INF);}return flow;
}int main(){int n,m;scanf("%d%d",&n,&m);//节点编号从1到n,边编号从0到m-1for(int i=1;i<=n;i++)G[i].clear();edges.clear();for(int i=0;i<m;i++){int u,v,w;scanf("%d%d%d",&u,&v,&w);addEdge(u,v,w);}S=1,T=n;printf("%d\n",Dinic());return 0;
}

【求最小割】

使用邻接数组与 STL 中的 set 容器,在求最大流的过程中,将最小割求出。

其原理是最大流最小割定理:网络的最大流的流量等于最小割的容量

struct Edge{int from,to;int cap,flow;Edge(){}Edge(int from,int to,int cap,int flow):from(from),to(to),cap(cap),flow(flow){}
};
int n,m;             //结点数,边数(含反向弧)
int S,T;             //源点、汇点
vector<Edge> edges;  //边表,edges[e]和edges[e^1]互为反向弧
vector<int> G[N];    //邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
bool vis[N];         //BFS使用,标记一个节点是否被遍历过
int dis[N];          //dis[i]表从起点s到i点的距离(层次)
int cur[N];          //cur[i]表当前正访问i节点的第cur[i]条弧
set<int> cutSet;     //最小割
bool flag;           //是否求最小割
void addEdge(int from,int to,int cap){edges.push_back( Edge(from,to,cap,0) );edges.push_back( Edge(to,from,0,0) );int m=edges.size();G[from].push_back(m-2);G[to].push_back(m-1);
}
bool BFS(){//构建层次网络memset(vis,0,sizeof(vis));dis[S]=0;vis[S]=true;//将超级源点加入最小割if(flag)cutSet.insert(S);queue<int> Q;//用来保存节点编号Q.push(S);while(!Q.empty()){int x=Q.front();Q.pop();for(int y=0;y<G[x].size();y++){Edge& e=edges[G[x][y]];if(!vis[e.to] && e.cap>e.flow){vis[e.to]=true;dis[e.to]=dis[x]+1;Q.push(e.to);if(flag)//记录最小割元素cutSet.insert(e.to);}}}return vis[T];
}int DFS(int x,int cp){//cp表示从s到x目前为止所有弧的最小残量if(x==T || cp==0)return cp;int flow=0,newFlow;//flow用来记录从x到t的最小残量for(int &y=cur[x];y<G[x].size();y++){Edge &e=edges[G[x][y]];if(dis[x]+1==dis[e.to]){int minn=min(cp,e.cap-e.flow);newFlow=DFS(e.to,minn);if(newFlow>0){e.flow+=newFlow;edges[G[x][y]^1].flow-=newFlow;flow+=newFlow;cp-=newFlow;if(cp==0)break;}}}return flow;
}
int Dinic(){int flow=0;while(BFS()){memset(cur,0,sizeof(cur));flow+=DFS(S,INF);}return flow;
}
int main(){scanf("%d%d",&n,&m);while(m--){int x,y,w;scanf("%d%d%d",&x,&y,&w);addEdge(x,y,w);}S=1,T=n;flag=false;//不统计最小割所属元素int minCut=Dinic();//计算最小割的值printf("The Min-Cut and Max-Flow:%d\n",minCut);flag=true;//统计最小割所属元素BFS();//构建层次网络printf("The Number of Min Cut:%d\n",cutSet.size());//最小割个数printf("The Min Cut Set:");set<int>::iterator it=cutSet.begin();for(;it!=cutSet.end();it++)//最小割元素printf("%d ",*it);printf("\n");return 0;
}

图论 —— 网络流 —— 最大流 —— Dinic 算法相关推荐

  1. 网络流最大流 Dinic算法模板

    Dinic算法跟EK算法的优化地方就在于,EK是每次bfs记录前驱结点,然后从汇点找到一条回到源点的路. 而dinic是bfs记录深度,不断从源点dfs到汇点并且更新路径流量,直到dfs不到汇点. 邻 ...

  2. hdu4280 Island Transport 网络流最大流 Dinic算法高效模板

    Island Transport In the vast waters far far away, there are many islands. People are living on the i ...

  3. 图论 —— 网络流 —— 费用流 —— MCMF 算法

    [概述] EK 算法是每次用广搜寻找一条最短的增广路,然后沿其增广,而 MCMF 算法是在 EK 算法的基础上,每次用 SPFA 计算图的距离标号,然后沿着可行边进行增广,即将 EK 算法中的 bfs ...

  4. 图论 —— 网络流 —— 最大流 —— FF 算法与 EK 算法

    [概述] FF 算法与 EK 算法是求解最大流的一般增广路方法,其时间复杂度均为 O(n*m*m) Ford-Fulkerson 算法是求解最大流的最基础的算法,其核心思想是增广路定理:网络达到最大流 ...

  5. 图论-网络流⑦-费用流解题

    图论-网络流⑦-费用流解题 上一篇:图论-网络流⑥-费用流 下一篇:图论-网络流⑧-有上下界的网络流 参考文献: https://www.luogu.com.cn/blog/user9012/solu ...

  6. 网络流——最大流EK算法讲解

    网络流--最大流EK算法讲解 好了,这是第二篇博客了,如第一篇所述,来讲一讲刚刚理解的网络流.因为本人只会EK算法,所以先讲这个算法.(我会去补知识点的!!!) 什么是网络流??? 读者们刚接触这个知 ...

  7. 网络流-最大流(Ford-Fulkerson算法Dinic算法)

    文章目录 最大流 FF算法: 增广路: 反边: 代码: Dinic算法: Dinic + 当前弧优化 最大流 就如同水流,存在一张图,既有起点又有终点,从起点流向终点的最大流量就是最大流. 在上面的图 ...

  8. 图论 —— 网络流 —— 费用流 —— zkw 费用流

    [概述] 求解费用流的方法很多,目前最流行的是 MCMF 费用流,其实质是将 EK 算法中的 bfs 换为了 SPFA 来计算最小费用,但其存在的一个缺点是 EK 是单路增广的,这样速度会相应的慢一些 ...

  9. UESTC 1143 数据传输 网络流 最大流 Dinic

    数据传输 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit  Sta ...

最新文章

  1. SGU 109 Magic of David Copperfield II
  2. 接收到开始本地截图的命令:videoshot.exe 解决方法
  3. 枚举(enum)类型
  4. 面试ASP.NET程序员的笔试题和机试题(转,有点意思,答案不是很标准)
  5. 【数据结构与算法】之深入解析“回文数”的求解思路和算法示例
  6. 每日一笑 | 程序员和产品经理打架了,怎么办?
  7. 攻防世界web2(逆向加密算法)
  8. IOT(23)---LoRa网络架构与终端装置运作图
  9. Windows下的SQL Server备份文件BAK在Linux环境下还原遇到的问题
  10. 查看设置本机共享文件 net share
  11. Multisium里如何使用多个不同的VCC
  12. Web.py session用户认证
  13. cin和cout比scanf和printf慢的原因及解决方案
  14. Matlab FontName 以及字体设置
  15. matlab三维极坐标天线方向图,天线方向图(Antenna Pattern)
  16. Excel ----- 身份证自动判断男女 公式
  17. 带你深度解析断点续传原理并案例Http1.1协议
  18. PS新手教程,教你打造一个炫酷的冰冻字效_桂桂博客
  19. 求最小公倍数c语言最简公式,C语言求最小公倍数和最大公约数三种算法(经典)...
  20. 国内外大厂扑向AR-HUD,但抵达“智能汽车终局”仍隔数层纱

热门文章

  1. 近日的思绪(外三首)
  2. 终于有人把数据、信息、知识讲明白了
  3. 华为天才少年1万块自制机械臂,网友:200万给少了
  4. CPU可以跑多快?地球到火星的距离告诉你!
  5. Linux Tomcat日志查看实用命令
  6. HDR【openCV实现】
  7. MNIST手写数字识别【Matlab神经网络工具箱】
  8. linux计划任务crond服务
  9. apache/nginx中equest.getServerName()外网环境获取不到代理地址/域名
  10. 不定宽高的div如何垂直居中