首页 > 代码库 > 关于网络流算法(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 }
View Code

 

 1.3 Bellman-Ford算法

由于在费用流中可能出现的负费用情况,必须考虑对含有负权边的有向图进行寻路的算法,一般采用Dijikstra或者Bellman-Ford算法,在这里也补充讨论下Bellman-Ford算法(下称B-F算法)。

基本的算法理解这篇文章对B-F算法介绍得已经很详细,另外《算导》Page379有更详细的证明可以参考 。在刘汝佳的《算法竞赛入门》中有进阶的算法实现,即使用FIFO队列辅助剪枝,这个方法在这篇文章中解释得很详细。

 

关于网络流算法(2)