原帖链接:点击进入
花了几天来学最大流问题,折腾的我头都晕了,下面就来让我做个总结。
首先介绍一下网络流,流网络G=(V,E)是一个有向图,其中每条边又有一个非负容量c(u,v)>=0,如果存在一条边(u,v)不属于G,那么可以认为c(u,v)=0。网络流中两个点,s(源点)和e(汇点)。
一、网络流的三个性质 :
1、容量限制: f(u,v)<=c(u,v)
2、反对称性:f(u,v) = - f(v,u)
3、流量平衡: 对于不是源点也不是汇点的任意结点,流入该结点的流量和等于流出该结点的流量和。
二、下面介绍一下残留网络和增广网络:
1、残留网络:对于网络G=(V,E,C),设流f是G中的流。残留网络是由还可以容纳更多的流的边组成。
Cf(u,v)=C(u,v)-f(u,v)
2、增广路:增广路p为残留网络Gf上的源s到汇e上一条简单路径。该路径的残留容量为可以沿该路径增加的额外流量。
三、最小割最大流定理:
网络 G = (V,E,C)中的割[S,T]将点集V划分为S、T两个部分,使得s(源点)在S中,而e(汇点)在T中。符号[S,T]代表集合{<u,v>|<u,v>,s属于S,e属于T},穿过割[S,T]的净流量定义为f(S,T),割[S,T]的容量定义为c(S,T)。
如果f是具有源s和汇e的网络G=(V,E,C)中的一个流,则下列条件是等价的:
(1)f是G的一个最大流
(2)残留网络Gf不包含增广路径
(3)对G的某个割[S,T],有|f|=c(S,T)
下面来介绍3种算法:
一、Edmond-Karp:
1、在残留网络中找到一条从源点到汇点的增广路,如果找到转向(2),没有转向(4)。
2、找到路径中最小的边:min,记录下来,累加到最大流中转向(3)。
3、增广路中所有c<u,v>减去min,所有c<u,v>加上min,重构新的残留网络,转向(2)。
4、得到最大流
#include<iostream> using namespace std; const int inf =0x7ffffff; const int MAXN = 210; int map[MAXN][MAXN],n,val[MAXN],queue[MAXN]; bool bfs(int s,int e) { int top(0),base(0); bool vis[MAXN]; memset(vis,false,sizeof(vis)); memset(val,-1,sizeof(val)); queue[top++] = s; vis[s] = true; while(top!=base) { int a = queue[base]; if(a == e) return true; base++; for(int i=1;i!=n+1;++i) { if(map[a][i]&&!vis[i]) { vis[i] = true; val[i] = a; queue[top++] = i; } } } return false; } int main() { int e; while(cin>>e>>n) { int u,flow(0),max; memset(map,0,sizeof(map)); while(e--) { int a,b,c; cin>>a>>b>>c; map[a][b]+=c; } while(bfs(1,n)) { max = inf; u = n; while(val[u]!=-1) { max = (max>map[val[u]][u])?map[val[u]][u]:max; u = val[u]; } flow+=max; u = n; while(val[u]!=-1) { map[val[u]][u]-=max; map[u][val[u]]+=max; u = val[u]; } } cout<<flow<<endl; } }
二、Dinic:
1.分层:利用BFS搜索方式给每个节点给予标记level[i];
2.判断分层是否成功即汇点是否被分到级别level[sink]!=0;
3.在分层的基础上即level[i]上寻找所有的增广路、累计流量,回到步骤1;
#include<iostream> #include<queue> using namespace std; const int inf = 0x7fffffff; const int MAXN = 210; int map[MAXN][MAXN]; int level[MAXN]; int n, m; int s, e; bool bfs()//对顶点进行标号,找出层次图 { queue<int> Q; while (!Q.empty()) Q.pop(); memset(level, 0, sizeof(level)); Q.push(s); level[s] = 1; int u, v; while (!Q.empty()) { u = Q.front(); Q.pop(); for (v = 1; v <= n; ++v) { if (!level[v] && map[u][v] > 0) { level[v] = level[u] + 1; Q.push(v); } } } return level[e] != 0;//汇点是否在层次图中 } int dfs(int u, int cp)//在层次图中寻找增广路径进行增广 { int tmp = cp; int v, t; if (u == e) return cp; for (v = 1; v <= n && tmp; ++v) { if (level[v] == level[u] + 1 && map[u][v] > 0) { t = dfs(v, min(tmp, map[u][v])); map[u][v] -= t; map[v][u] += t; tmp -= t; } } return cp - tmp; } int dinic() { int ans, flow; ans = flow = 0; while (bfs())//汇点不在层次图中,算法终止 { while (flow = dfs(1, inf)) { ans += flow; } } return ans; } int main() { int i, u, v, cost; while(cin>>m>>n) { s = 1; e = n; memset(map, 0, sizeof(map)); for(i = 0; i < m; i++) { cin >> u >> v >> cost; map[u][v] += cost; } cout<<dinic()<<endl;; } return 0; }
三、SAP+GAP
1.给每个点标高度 只有高处的水才能往地处流 一开始的高度都为0
2.在所有的可行弧中不断的寻找增广路 可行弧的定义为 {(i,j) | dis[i]=dis[j]+1}
3.遍历完当前节点后(流不出去了) 重新标记当前点的高度(保证下次再来的时候有路可走) dis[i]=min(dis[j])+1;
4.检查是否存在断层 如果出现断层 则图中已不存在增广路 算法可以结束 否则从源点开始继续遍历
#include<iostream> using namespace std; const int inf = 0x7fffffff; const int MAXN = 210; const int MAXM = MAXN*MAXN; int map[MAXN][MAXN]; int dis[MAXN]; int queue[MAXM]; int bef[MAXN]; int num[MAXN]; void set_dis(int end) { int top(0),base(0); memset(dis,-1,sizeof(dis)); memset(num,0,sizeof(num)); queue[top++] = end; dis[end]=0;num[0] = 1; while(top!=base) { int pre = queue[base++]; for(int i=end-1;i!=0;i--) { if(dis[i] != -1 || map[i][pre] == 0) continue; dis[i] = dis[pre]+1; queue[top++] = i; num[dis[i]]++; } } } int cmp(int x,int y) { if(x<y) return x; return y; } int remark(int n,int a) { int tmp=inf; for(int i=1;i!=n+1;i++) if(map[a][i]!=0&&dis[i]>=0) tmp=cmp(tmp,dis[i]+1); if(tmp==inf) tmp=n; return tmp; } int SAP(int n,int s) { int j,a = s;int flow(0); memset(bef,-1,sizeof(bef)); while(dis[s]<n) { for(j=1;j!=n+1;j++) { if(map[a][j]>0&&dis[a]-1==dis[j]) { break; } } if(j<=n) { bef[j] = a;a = j; if(a==n) { int i = n,max(inf); while(bef[i]!=-1) { max = cmp(max,map[bef[i]][i]); i = bef[i]; } i = n; while(bef[i]!=-1) { map[bef[i]][i]-=max; map[i][bef[i]]+=max; i = bef[i]; } flow+=max;a=s; } } else { int x = remark(n,a); num[x]++;num[dis[a]]--; if(num[dis[a]]==0) return flow;//间隙优化 gap dis[a]=x; if(a!=s) a=bef[a]; } } return flow; } int main() { int e,n; //cout<<inf<<endl; while(cin>>e>>n) { memset(map,0,sizeof(map)); while(e--) { int a,b,w; cin>>a>>b>>w; map[a][b] += w; } set_dis(n); cout<<SAP(n,1)<<endl; } return 0; }