首页 > 代码库 > 关于网络流算法(2)
关于网络流算法(2)
了解最大流解法:
网络流的相关基础知识很容易获得,详细的有《算导》,简单的有刘汝佳《算法竞赛入门》,这里选用的也是刘的书从Page207开始的内容。
这里要补充一些值得注意的基础:
- 最大流问题中的三个约束条件:容量限制条件、斜对成性条件、流量平衡条件;
- 网络流问题中边(Edge)的有向性。
1.1 BFS算法
因为《算法竞赛入门》中的E-K算法是基于BFS遍历方法的,说是很容易找到让DFS很慢的例子所以改为BFS,自己又已经把以前看的书全部忘记了,无奈只能开这个1.1,非常惭愧。
BFS,Broad First Search,广度优先搜索算法,或,层序遍历算法。算法的理解部分只需要记住一句话:依次访问已经访问过的顶点的未访问临接顶点。、
实现层面,由于对于节点的遍历是顺序的、FIFO的,所以使用队列辅助进行顺序控制。
用邻接矩阵实现的BFS算法代码如下:
1 void bfs(int s, bool edge[][maxn]) 2 { 3 visit(s,visited); 4 q.push(s); 5 while(!q.empty()) 6 { 7 s = q.front(); 8 q.pop(); 9 for(int w = next_Neighbor(s,0,edge);w>=0;w=next_Neighbor(s,w,edge)) 10 { 11 if(!visited[w]) 12 { 13 visit(w,visited); 14 q.push(w); 15 } 16 } 17 } 18 }
1.2 Edmonds-Karp算法(增广路算法)
E-K算法是最基础的最大流算法,主要思想是从最小流状态迭代增加流量,直到不能继续增加达到稳态。
其中迭代的过程称为“增广”,即对于某个s到t的流量通路,如果其上各边流量都没有达到各自的容量(流量上限),那么就在这条通路上增加这些边中的最小残量单位的流量。
E-K算法涉及到一个反向更新流量的问题,需要在代码中自己体会。
这里我贴个随笔,觉得写得很接地气。这篇文章中关于反向边权值的表达是“退回”,这个在《算法竞赛入门》也有部分表述,可以结合在一起看,另外补充一个比较全面的教程。
使用邻接矩阵实现的代码如下:
1 /* 2 不断寻找残量网络中汇点的可达路径, 3 可达路径中的各条边每次迭代减少可达路径中所有边 capacity 最小值直到在残量网络中汇点不可达 4 */ 5 6 #include<iostream> 7 #include<queue> 8 using namespace std; 9 const int arrsize = 10; 10 const int maxdata =http://www.mamicode.com/ INT_MAX; 11 12 int cap[arrsize][arrsize]; //表示两个结点之间的最大可用流量 13 int flow[arrsize],pre[arrsize]; //表示从原点到index结点的当前剩余可用流量 14 queue<int> q; 15 16 17 int BFS(int src, int des) 18 //BFS函数,寻找可行通路 19 { 20 //cout<<endl<<"进入BFS"<<endl; 21 while(!q.empty()) 22 q.pop(); 23 24 memset(pre,-1,sizeof(pre)); 25 26 pre[src] = 0; 27 flow[src] = maxdata; 28 29 q.push(src); 30 while(!q.empty()) 31 { 32 int index = q.front(); 33 q.pop(); 34 35 if(index == des) 36 break; 37 //层序寻找下一个可行通路 38 for(int i=0;i<arrsize;i++) 39 { 40 if(i!=src && cap[index][i]>0 && pre[i]==-1) 41 { 42 pre[i]=index; // 保存当前结点的父结点 43 flow[i]=min(cap[index][i],flow[index]); //★更新从源结点到当前结点的当前剩余可用流量 44 q.push(i); 45 } 46 } 47 } 48 if(pre[des]==-1) 49 return -1; 50 else 51 return flow[des]; 52 } 53 54 int maxFlow(int src, int des) 55 //s最大流函数,迭代残量网络 56 { 57 int increasement,sumfolw = 0; 58 while((increasement = BFS(src,des)) != -1) 59 { 60 int cur = des; 61 while(cur != src) 62 { 63 int last = pre[cur]; 64 cap[last][cur] -= increasement; 65 cap[cur][last] += increasement; 66 cur=last; 67 } 68 sumfolw += increasement; 69 } 70 return sumfolw; 71 } 72 73 74 75 int main() 76 { 77 freopen("C:\\Users\\lenovo\\Desktop\\工作\\华为挑战赛\\数据_E-K.txt","r",stdin); 78 79 for(short i=0;i<arrsize;i++) 80 for(short j=0;j<arrsize;j++) 81 cin>>cap[i][j]; 82 83 //打印邻接矩阵 84 cout<<"邻接矩阵(有向图)为:"<<endl; 85 for(short i=0;i<arrsize;i++) 86 for(short j=0;j<arrsize;j++) 87 { 88 cout<<cap[i][j]<<" "; 89 if(j==arrsize-1) 90 cout<<endl; 91 } 92 cout<<endl; 93 94 memset(flow,0,sizeof(flow)); 95 cout<<"最大流为:"<<maxFlow(0,3)<<endl<<endl; 96 97 fclose(stdin); 98 }
1.3 Bellman-Ford算法
由于在费用流中可能出现的负费用情况,必须考虑对含有负权边的有向图进行寻路的算法,一般采用Dijikstra或者Bellman-Ford算法,在这里也补充讨论下Bellman-Ford算法(下称B-F算法)。
基本的算法理解这篇文章对B-F算法介绍得已经很详细,另外《算导》Page379有更详细的证明可以参考 。在刘汝佳的《算法竞赛入门》中有进阶的算法实现,即使用FIFO队列辅助剪枝,这个方法在这篇文章中解释得很详细。
关于网络流算法(2)