一直觉得网络流很吊。。。然后就从网上找了个pdf来看
《图论与网络流》
这本书里讲述了图论的相关知识和定理。还有相关证明
特别是网络流那一章。讲的通俗易懂。。
目前为止只看了Ford-Fulkerson算法和Dinic算法
基本上看了一两遍就懂了。。
Ford-Fulkerson算法:
1)找到一条可增广的x-y路,并得出这条路上可增广的最小值min(非零)
2)沿着这条可增广的x-y路,对于这条增广路中所有边。有以下两个操作
1.如果这条边是正向的,边权减去min
2.如果这条边是方向的,边权加上min
3)重复1)&2)直到没有增广路
主体思想是这个。
具体的步骤为:
V–> 所有点集合
x–> 源点集合
y–> 汇点集合
A–> 边的流量集合
C–> 边的容量集合
F(x,y)–> x->y这条边的流量
L–> 已标未查集合
S–> 已标已查集合
给定网络 N(V,x,y,A,C)
求最大流的步骤:
1)先把所有边的流量清空为0
2)给所有源点x标号为(x,无穷大),把x放到已标未查集里
3)从已标未查集中取出u,检查u的未标号的邻点v
1.若u–>v是正向边,并且可以有流量通过(F(u,v)<C(u,v)),给v标号为(u,+,l(v)),l(v)=min(l(u),C(u,v)-F(u,v))表示为从源点到v这个点花的最小流量(因为要加上),把v放进已标未查集
2.如果u–v是反向边,并且F(u,v)>0 给v点标号为(u,-,l(v)),l(v)=min(l(u),C(u,v)-F(u,v)),把v放到已标未查集
4)把u放到已标已查集,若汇点v被标号,走第6步,否则走第5步
5)如果已标未查集为空,算法结束,当前流是最大流;否则转第二步
6)如果v是汇点,转下一步
7)如果是已标号的汇点,如果标号为(w,+,l(v)) 那么F(w,v)+=l(v),如果标号为
(w,-,l(v))那么F(w,v)-=l(v)
8)另v=w,转第7步,否则去除所有点的标号。转第2步
算法结束
如果看懂了这个算法的话。很容易想到bfs 记录路径 处理路径这几个关键点吧。。
(这个代码没有写过,只是大概知道应该这么回事,因为我看了Dinic就放弃学习Ford-Fulkerson了)
优化过后的Ford-Fulkerson算法复杂度为O(ve2) , e–>边的数量
Dinic算法:
这个算法思路跟Ford-Fulkerson的差不多。但是优化了Ford-Fulkerson的查找路径过程
算法复杂度为O(v2e)还是比较可观。看起来挺吓人。但实际是到达不了的
1)构造分层网络(从源点开始的最短路,每条边的边假设为1)
给每个点标号分层,如果汇点没有获得标号,那么结束算法
假设从源点到某个点的最短路为x,那么所有从源点到某个点最短路为x的点都分为一层
2)构造辅助网络N(v)
在分层网络的基础上。对于所有的边
1.如果F(u,v)<C(u,v) 那么F′(u,v)=C(u,v)−F(u,v) , F′(v,u)=F(u,v)
2.如果F(u,v)==C(u,v)那么F′(u,v)=C(u,v) , F′(v,u)=0
在分层网络的基础上在原图中删去所有同层之间的边(也不一定是删除,主要是只保留从底层到高层的边)
3)在辅助网络中找到一条x-y增广路(这条增广路上可增广的流为x>0)
对于x-y增广路上的所有边
1.如果边为正向 那么加上x
2.如果边为方向 那么减去x
4)转1
具体的步骤也没有什么好说的了。
值得一提的是 在3)这个步骤中,可以使用栈的方式来处理。
假设我找到了一条x-y增广路 1–>2–>3–>4–>5–>6
F(1,2)=10 F(2,3)=20 F(3,4)=5 F(4,5)=30 F(5,6)=40
我们可以得到这条x-y路上的最小可增广的流为5,它是3–>4这条边上的
我们可以先把这条x-y路上的所有正向边减去5 所有反向边加上5
再从3这个地方开始往下找增广路(有种东东叫做栈)
如果看到了这里。看懂了的话。完全可以自己敲出代码。。
讲到这里好了。。好累。敲了半天。= =
Dinic代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <set> #include <vector> #include <map> #include <queue> #include <set> #include <algorithm> #include <limits.h> using namespace std; #define MAXN 10000 #define MAXM 100000 const int INF = INT_MAX; typedef long long LL; struct Edge { int from,to,ci,next; Edge(){} Edge(int _from,int _to,int _ci,int _next):from(_from),to(_to),ci(_ci),next(_next){} }e[MAXM]; int head[MAXN],tot; int dis[MAXN]; int top,sta[MAXN],cur[MAXN]; int n,m; inline void init(){ memset(head,-1,sizeof(head)); tot=0; } inline void AddEdge(int u,int v,int ci0,int ci1=0){ e[tot]=Edge(u,v,ci0,head[u]); head[u]=tot++; e[tot]=Edge(v,u,ci1,head[v]); head[v]=tot++; } inline bool bfs(int st,int et){ memset(dis,0,sizeof(dis)); dis[st]=1; queue <int> q; q.push(st); while(!q.empty()){ int now=q.front(); q.pop(); for(int i=head[now];i!=-1;i=e[i].next){ int next=e[i].to; if(e[i].ci&&!dis[next]){ dis[next]=dis[now]+1; if(next==et)return true; q.push(next); } } } return false; } LL Dinic(int st,int et){ LL ans=0; while(bfs(st,et)){ top=0; memcpy(cur,head,sizeof(head)); int u=st,i; while(1){ if(u==et){ int pos,minn=INF; printf("top:%d\n",top); for(i=0;i<top;i++) { if(minn>e[sta[i]].ci){ minn=e[sta[i]].ci; pos=i; } } for(i=0;i<top;i++){ e[sta[i]].ci-=minn; e[sta[i]^1].ci+=minn; } top=pos; u=e[sta[top]].from; ans+=minn; } for(i=cur[u];i!=-1;cur[u]=i=e[i].next) if(e[i].ci&&dis[u]+1==dis[e[i].to])break; if(cur[u]!=-1){ sta[top++]=cur[u]; u=e[cur[u]].to; } else { if(top==0)break; dis[u]=0; u=e[sta[--top]].from; } } } return ans; } int ru[MAXN],chu[MAXN]; int main() { while(~scanf("%d%d",&n,&m)){ init(); for(int i=0,x,y,vi,ci;i<m;i++){ scanf("%d%d%d%d",&x,&y,&vi,&ci); AddEdge(x,y,ci-vi,vi); ru[y]++; chu[x]++; } int st,et; for(int i=1;i<=n;i++){ if(ru[i]==0)st=i; if(chu[i]==0)et=i; } printf("st:%d et:%d\n",st,et); printf("%I64d\n",Dinic(1,n)); } return 0; }
|