第一次接触网络流,感觉像是一堆的算法扑面而来,三天就要过去了,才刚刚对几个算法有了点初步的理解,感觉上离要熟练的做出题还很遥远,这里先给出小编对几个算法的模板总结。
1.Edmond-Karp算法
这种算法是最好理解的,网络上也有很多的对此算法的讲解,这里小编就不给出详细的讲解。算法的关键就是不停的在残留网络中找到增广路径,并不停的修改残留网络中的值,最后知道找不到增广路径为止,得到最大流。
#include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; const int maxn = 200+2; const int INF = 0xfffffff; int capacity[maxn][maxn],flow[maxn],pre[maxn],n,m; queue<int> q; int BFS(int src,int des) { int i,j; while(!q.empty()) q.pop();//队列清空 for(i=1; i<=m; i++) pre[i] = -1; pre[src] = 0; flow[src] = INF; q.push(src); while(!q.empty()) { int index = q.front(); q.pop(); if(index == des) break;//找到了增广路径 for(i=1; i<=m; i++) { if(i!=src && capacity[index][i]>0 && pre[i]==-1) { pre[i] = index;//记录前驱 flow[i] = min(capacity[index][i],flow[index]); q.push(i); } } } if(pre[des] == -1) return -1;//残留图中不再存在增广路径 else return flow[des]; } int maxFlow(int src,int des) { int increasement = 0; int sumflow = 0; while((increasement = BFS(src,des)) != -1) { int k = des;//利用前驱寻找路径 while(k != src) { int last = pre[k]; capacity[last][k] -= increasement;//改变正向边的容量 capacity[k][last] += increasement;//改变反向边的容量 k = last; } sumflow += increasement; } return sumflow; } int main() { int i,j; int start,end,ci; while(scanf("%d%d",&n,&m)!=EOF) { memset(capacity,0,sizeof(capacity)); memset(flow,0,sizeof(flow)); for(i=0; i<n; i++) { scanf("%d%d%d",&start,&end,&ci); if(start == end) continue; capacity[start][end] += ci; } printf("%d\n",maxFlow(1,m)); } return 0; }
网络流的算法大都可以分成两种方式书写,这里小编给出的是SPA的邻接图的算法,有关SPA,一个重要的优化就是gap优化,达到寻找断路停止搜索的目的。
#include <cstdio>//网络流SPA算法(邻接矩阵) #include <iostream> #include <cstring> using namespace std; const int maxn = 222; const int INF = 0xfffffff; int map[maxn][maxn]; int pre[maxn]; int level[maxn]; int gap[maxn]; int NV,NE; int SAP(int vs,int vt) { memset(pre,-1,sizeof(pre)); memset(level,0,sizeof(level)); memset(gap,0,sizeof(gap)); gap[0] = vt; int v,u=pre[vs]=vs,maxflow=0,aug=INF; while(level[vs] < vt) {//寻找可行弧 for(v=1; v<=vt; v++) if(map[u][v]>0 && level[u]==level[v]+1) break; if(v <= vt) { pre[v] = u; u = v; if(v == vt) { aug = INF; for(int i=v; i!=vs; i=pre[i]) if(aug > map[pre[i]][i]) aug = map[pre[i]][i]; maxflow += aug; for(int i=v; i!=vs; i=pre[i]) { map[pre[i]][i] -= aug; map[i][pre[i]] += aug; } u = vs; } } else { int minlevel = vt; for(v=1; v<=vt; v++) if(map[u][v]>0 && minlevel>level[v]) minlevel = level[v]; gap[level[u]]--; if(gap[level[u]] == 0) break; level[u] = minlevel+1; gap[level[u]]++; u = pre[u]; } } return maxflow; } int main() { int n,m,u,v,cap; while(scanf("%d%d",&m,&n)!=EOF) { memset(map,0,sizeof(map)); for(int i=1; i<=m; i++) { scanf("%d%d%d",&u,&v,&cap); map[u][v] += cap; } printf("%d\n",SAP(1,n)); } return 0; }
这里小编给出的是Dinic的邻接图模板。
#include <cstdio>//网络流Dinic算法(邻接图) #include <cstring> #include <queue> using namespace std; const int maxn = 210; const int INF = 0xfffffff; 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(scanf("%d%d",&m,&n)) { s = 1; e = n; memset(map,0,sizeof(map)); for(i=0; i<m; i++) { scanf("%d%d%d",&u,&v,&cost); map[u][v] += cost; } printf("%d\n",dinic()); } return 0; }