网络流之最大流 EK/Dinic/Isap算法 学习笔记
来源:互联网 发布:学科数据分析 编辑:程序博客网 时间:2024/06/06 16:37
EK
算法流程
不停地找增广路进行增广,知道找不到增广路为止。
每一次bfs只找一条增广路。
时间复杂度
代码
// codevs 1993 #include<iostream>#include<cstring>#include<cstdio>#include<queue> using namespace std;const int inf=2100000000;int n,m,maxflow,a[205][205],flow[205],pre[205];//n表示边数,m表示点数,maxflow为最大流流量,a为每条边的容量,flow为每个点增广的流量,pre为增广时点的前驱 int x,y,cap;queue <int> q;inline int bfs(int s,int t){ while (!q.empty()) q.pop(); for (int i=1;i<=m;++i) pre[i]=-1; pre[s]=0; q.push(s); flow[s]=inf; while (!q.empty()){ int x=q.front(); q.pop(); if (x==t) break; for (int i=1;i<=m;++i) //EK一次只找一个增广路 if (a[x][i]>0&&pre[i]==-1){ pre[i]=x; flow[i]=min(flow[x],a[x][i]); q.push(i); } } if (pre[t]==-1) return -1; else return flow[t];}//s为源点,t为汇点 //increase为增广的流量 inline void ek(int s,int t){ int increase=0; while ((increase=bfs(s,t))!=-1){ int k=t; while (k!=s){ int last=pre[k]; a[last][k]-=increase; a[k][last]+=increase; k=last; } maxflow+=increase; }}int main(){ scanf("%d%d",&n,&m); for (int i=1;i<=n;++i){ scanf("%d%d%d",&x,&y,&cap); a[x][y]+=cap; } ek(1,m); printf("%d\n",maxflow);}
Dinic
dinic算法在EK算法的基础上增加了分层图的概念,根据从s到各个点的最短距离的不同,把整个图分层。寻找的增广路要求满足所有的点分别属于不同的层,且若增广路为
算法流程
- 对网络中的每一条边,将流量设置为0
- 对当前残量网络,构建分层图。若
deept=+∞ ,则退出,输出答案。 - 寻找到残量网络中的一条满足分层图限制的可行流
(即原网络中满足分层图限制的增广路) - 利用寻找到的增广路『增流』 ,重复步骤2
时间复杂度
在普通情况下, DINIC算法时间复杂度为
在二分图中, DINIC算法时间复杂度为
优化
• 多路增广
每次不是寻找一条增广路,而是在DFS中,只要可以就递归增广下去,实际上形成了一张增广网。
• 当前弧优化
对于每一个点,都记录上一次检查到哪一条边。因为我们每次增广一定是彻底增广(即这条已经被增广过的边已经发挥出了它全部的潜力,不可能再被增广了),下一次就不必再检查它,而直接看第一个未被检查的边。
优化之后渐进时间复杂度没有改变,但是实际上能快不少。
实际写代码的时候要注意,next数组初始值为-1,存储时从0开始存储,这样在后面写反向弧的时候比较方便,直接异或即可。
代码
// codevs 1993#include<iostream>#include<cstring>#include<cstdio>#include<queue>using namespace std;const int max_n=205;const int max_m=205;const int max_e=max_m*2;const int inf=1e9;int point[max_n],next[max_e],v[max_e],remain[max_e],deep[max_n],cur[max_n];int n,m,x,y,cap,tot,maxflow;queue <int> q;inline void add(int x,int y,int cap){ ++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=cap; ++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0;}//分层 inline bool bfs(int s,int t){ //初始化 memset(deep,0x7f,sizeof(deep)); deep[s]=0; for (int i=1;i<=n;++i) cur[i]=point[i]; while (!q.empty()) q.pop(); q.push(s); while (!q.empty()){ int now=q.front(); q.pop(); for (int i=point[now];i!=-1;i=next[i]) if (deep[v[i]]>inf&&remain[i]){ deep[v[i]]=deep[now]+1; q.push(v[i]); } } return deep[t]<inf;}//找到当前点最大能够增广的flow //limit表示到目前为止走过的增广路容量最小的边 inline int dfs(int now,int t,int limit){ if (!limit||now==t) return limit; int flow=0,f; for (int i=cur[now];i!=-1;i=next[i]){ cur[now]=i; if (deep[v[i]]==deep[now]+1&&(f=dfs(v[i],t,min(limit,remain[i])))){ flow+=f; limit-=f; remain[i]-=f; remain[i^1]+=f; if (!limit) break; } } return flow;}inline void dinic(int s,int t){ while (bfs(s,t)) maxflow+=dfs(s,t,inf);}int main(){ tot=-1; memset(point,-1,sizeof(point)); memset(next,-1,sizeof(next)); scanf("%d%d",&m,&n); for (int i=1;i<=m;++i){ scanf("%d%d%d",&x,&y,&cap); add(x,y,cap); } dinic(1,n); printf("%d\n",maxflow);}
Isap
ISAP(ImprovedShortestAugmentingPath)也是基于分层思想的最大流算法。所不同的是,它省去了
Dinic每次增广后需要重新构建分层图的麻烦,而是在每次增广完成后自动更新每个点的标号(也就是所
在的层)
算法流程
- 利用BFS从开始反向标号(分层)。
- 进行递归,若当前节点
i
(1)为汇点,则进行增广,同时退回到起点准备进行新一轮增广路的寻找。
(2)在残量网络中存在一条边(i,j) ,c′ij>0 ,且deepi=deepj+1 (即满足分层图的要求),则
前进到
3. 没有满足条件的出边,对
中
算法结束的条件:
时间复杂度
渐进时间复杂度和dinic相同,但是非二分图的情况下isap更具优势。
优化
1、当前弧优化:和dinic相同
2、GAP优化:
若
代码
// codevs 1993#include<iostream>#include<cstring>#include<cstdio>#include<queue>using namespace std;const int max_n=205;const int max_m=205;const int max_e=max_m*2;const int inf=1e9;int point[max_n],next[max_e],v[max_e],remain[max_e],tot;int cur[max_n],deep[max_n],last[max_n],num[max_n];int n,m,x,y,cap,maxflow;queue <int> q;inline void add(int x,int y,int cap){ ++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=cap; ++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0;}inline void bfs(int t){ for (int i=1;i<=n;++i) deep[i]=n; deep[t]=0; while (!q.empty()) q.pop(); q.push(t); while (!q.empty()){ int now=q.front(); q.pop(); for (int i=point[now];i!=-1;i=next[i]) if (deep[v[i]]==n&&remain[i^1]){ deep[v[i]]=deep[now]+1; q.push(v[i]); } }}inline int addflow(int s, int t) { int ans=inf,now=t; while (now!=s) { ans=min(ans, remain[last[now]]); now=v[last[now] ^ 1]; } now=t; while (now != s) { remain[last[now]]-=ans; remain[last[now]^1]+=ans; now=v[last[now]^1]; } return ans;}inline void isap(int s,int t){ int now=s; bfs(t); for (int i=1;i<=n;++i) ++num[deep[i]]; for (int i=1;i<=n;++i) cur[i]=point[i]; //在残量网络中没有源点到汇点的通路 while (deep[s]<n){ //如果到达汇点则进行增广,重新回到源点准备下一轮增广 if (now==t){ maxflow+=addflow(s,t); now=s; } bool has_find=false; //当前弧优化 for (int i=cur[now];i!=-1;i=next[i]){ int u=v[i]; if (deep[u]+1==deep[now]&&remain[i]){ has_find=true; cur[now]=i; last[u]=i; now=u; break; } } //没有找到出边,重新进行标号 if (!has_find){ int minn=n-1; for (int i=point[now];i!=-1;i=next[i]) if (remain[i]) minn=min(minn,deep[v[i]]); //GAP优化 if (!(--num[deep[now]])) break; num[deep[now]=minn+1]++; cur[now]=point[now]; if (now!=s) now=v[last[now]^1]; } }}int main(){ tot=-1; memset(point,-1,sizeof(point)); memset(next,-1,sizeof(next)); scanf("%d%d",&m,&n); for (int i=1;i<=m;++i){ scanf("%d%d%d",&x,&y,&cap); add(x,y,cap); } isap(1,n); printf("%d\n",maxflow);}
- 网络流之最大流 EK/Dinic/Isap算法 学习笔记
- 网络流算法总汇(ek,dinic,isap)
- 最大流算法,Dinic,ISAP
- 网络流模板:最大流ISAP算法和Dinic算法
- 网络流模板:最大流ISAP算法和Dinic算法
- poj_1459 Power Network(多源多汇最大网络流)(EK / dinic / ISAP)
- poj1273(网络流最大流 EK算法&&dinic算法)
- POJ 1273 网络流(EK,Dinic,ISAP)
- 网络流最大流EK和Dinic入门算法
- 图论算法-网络最大流【EK;Dinic】
- : 最大流(Dinic算法 + ISAP算法)
- 最大流ISAP+Dinic
- 网络流之最大流(FF, EK, Dinic, SAP)
- hdu3549 Flow Problem 网络最大流的三种写法(Ek,Dinic(邻接矩阵,邻接表),Isap)
- 最大流EK和Dinic算法
- poj1698-网络流,(Ek)和(Dinic)算法。
- 网络流最大流算法(ISAP算法及DINIC算法)
- isap算法网络最大流
- Nagios远程监控windows方案补充说明
- Mac下Android Studio使用github
- leetcode 10. Regular Expression Matching
- 【C语言】PCM音频数据处理---音量增大或减小
- c++中的引用
- 网络流之最大流 EK/Dinic/Isap算法 学习笔记
- ksh自动补全
- iOS学习----------详解FFMPEG API
- Activity生命周期的简单分析
- TCP/IP协议簇
- Windows 实用系统管理命令
- C#+Hybrid App(Android)实现微信APP支付
- MATLAB总览
- 程序员的工匠精神