这两天复习了下网络流,高中学的ISAP忘得七七八八,干脆把Dinic,ISAP和预流推进都重新看了一遍,写了个简洁的小结(基本上就是给自己看的)
网络流
剩余图
顶点的层次:源点到点的最短路径长度
层次图建立在剩余图基础上
阻塞流:不存在增广路时
FF
复杂度O(n*m*u)
容量网络、流量网络、残量网络
用最大流最小割定理证明
EK
复杂度O(n*m*m)
建反向边,每次bfs找最短增广路
最多增广n*m次(每阶段最多增广m次)
Dinic
多路增广
1.初始化容量网络和网络流
2.构造残留网络和层次网络,汇点不在则退出
3.一次DFS以增广
4.转2
比起EK单阶段增广从m*m优化为n*m
最高标号预流推进(HLPP)
预流推进:
定义盈余、活跃节点、h函数、可推流边(h(u)=h(v)+1)
1.令flow(s,v)=cap(s,v),flow(u,v)=0,构造h函数,h(s)=n
2.不存在活顶点则exit
3.选取活顶点v,选可推流边,没有了则令h(v)=min{h(w)+1},再加入队列
FIFO队列n*n*m
先进先出则变为最高标号预流推进,n*n*sqrt(m)
流量一次性从源中出来,推入汇或最终退回源
ISAP
Improved Shortest Augmenting Path
在EK算法基础上,增广后继续增广,直到遇到死路,执行retreat操作(修改d值,重建层次网络)
retreat时d[u]=min{d[v]+1}(u,v是残量网络邻接点,v靠t更近)(保证了最短路),若无邻接点,取d[u]大于等于n
实现:
1.初始化d数组,从t到s逆向进行
2.维护当前节点u
3.不连通时d[s]>=n
4.GAP 优化(重要):retreat时若u是唯一一个和t距离d[u]的点,则exit
5.另一优化:记录上次处理到的邻接边
拿poj1273写了Dinic和ISAP的模板(基本上去掉main就是模板内容)。虽然A了,但可能还有错误,以后做网络流的有发现时候再更正,有空再写个预流推进。
当前ISAP的模板是递归的,以后再多写几个版本出来。
Dinic
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define rep(i,a,b) for (int i=a;i<=b;++i) #define tra(k,u) for (int k=head[u];k!=-1;k=ed[k].next) const int MAXN=2000; const int MAXM=2000; const int INF=1e9; struct edge{ int to,f,next; }ed[2*MAXM+10]; int head[MAXN],tot; int d[MAXN]; int q[MAXN]; int n,m,s,e,c,ans; void init(int point_num) { tot=-1; memset(head,-1,sizeof(int)*(point_num+1)); } void add_edge(int u,int v,int f) { ed[++tot].to=v;ed[tot].f=f;ed[tot].next=head[u];head[u]=tot; ed[++tot].to=u;ed[tot].f=0;ed[tot].next=head[v];head[v]=tot; } void bfs(int src) { int l=0,r=0,now; q[++r]=src;d[src]=0; while (l<r) { now=q[++l]; tra(k,now) if (ed[k].f && d[ed[k].to]==-1) { d[ed[k].to]=d[now]+1; q[++r]=ed[k].to; } } } int dfs(int now,int delta,int t) { if (now==t) return delta; int tempflow=0,stream=0; tra(k,now) if (ed[k].f && d[ed[k].to]==d[now]+1) { stream=dfs(ed[k].to,min(delta,ed[k].f),t); tempflow+=stream; ed[k].f-=stream; ed[k^1].f+=stream; delta-=stream; if (delta==0) break; } return tempflow; } int maxflow_Dinic(int s,int t,int point_num) { int maxflow=0; while (1) { memset(d,-1,sizeof(int)*(point_num+1)); bfs(s); if (d[t]==-1) return maxflow; maxflow+=dfs(s,INF,t); } } int main() { while(~scanf("%d%d",&n,&m)) { init(m); rep(i,1,n) { scanf("%d%d%d",&s,&e,&c); add_edge(s,e,c); } ans=maxflow_Dinic(1,m,m); printf("%d\n",ans); } }
递归ISAP
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define rep(i,a,b) for (int i=a;i<=b;++i) #define tra(k,u) for (int k=head[u];k!=-1;k=ed[k].next) const int MAXN=2000; const int MAXM=2000; const int INF=1e9; struct edge{ int to,f,next; }ed[2*MAXM+10]; int head[MAXN],tot; int d[MAXN]; int q[MAXN]; int pre[MAXN]; //增广路上的上一个点 int gap[MAXN]; //gap优化 int n,m,s,e,c,ans; void init(int point_num) { tot=-1; memset(head,-1,sizeof(int)*(point_num+1)); } void add_edge(int u,int v,int f) { ed[++tot].to=v;ed[tot].f=f;ed[tot].next=head[u];head[u]=tot; ed[++tot].to=u;ed[tot].f=0;ed[tot].next=head[v];head[v]=tot; } void bfs(int t) { int l=0,r=0,now; q[++r]=t;d[t]=0; while (l<r) { now=q[++l]; tra(k,now) if (ed[k^1].f && d[ed[k].to]==-1) { d[ed[k].to]=d[now]+1; q[++r]=ed[k].to; } } } int dfs(int now,int delta,int t,int point_num) { if (now==t) return delta; int stream=0,tempflow=0,mind=point_num; tra(k,now) { if (ed[k].f) { if (d[ed[k].to]+1==d[now]) { stream=dfs(ed[k].to,min(delta,ed[k].f),t,point_num); tempflow+=stream; ed[k].f-=stream; ed[k^1].f+=stream; delta-=stream; if (d[s]>=point_num) return tempflow; if (delta==0) break; } mind=min(mind,d[ed[k].to]+1); } } if (tempflow==0) { --gap[d[now]]; if (gap[d[now]]==0) d[s]=point_num; d[now]=mind; ++gap[d[now]]; } return tempflow; } int maxflow_ISAP(int s,int t,int point_num) { int u=0,ans=0; memset(d,-1,sizeof(int)*(point_num+1)); memset(gap,0,sizeof(int)*(point_num+1)); bfs(t); //bfs可去掉,效率稍微降低 rep(i,1,n) ++gap[d[i]]; while (d[s]<point_num) { ans+=dfs(s,INF,t,point_num); } return ans; } int main() { while(~scanf("%d%d",&n,&m)) { init(m); rep(i,1,n) { scanf("%d%d%d",&s,&e,&c); add_edge(s,e,c); } ans=maxflow_ISAP(1,m,m); printf("%d\n",ans); } }