网络流 练习

来源:互联网 发布:大学生借款软件 编辑:程序博客网 时间:2024/05/21 22:39

题目:利用Ford_Fulkerson (标号法)求图1(a)及2(a)所示的容量网络的最大流,输出各条弧及其流量,以及求得的最大流流量。

  (1)

(2)

分析:

       在下面的程序中,以邻接矩阵存储容量网络,但邻接矩阵中的元素为结构体ArcType类型变量。该结构体描述了网络中弧的结构,包含容量c和流量f两个成员。在程序中,还定义了三个数组:flag[n], prev[n], alpha[n],其中:


1) flag[n]表示顶点状态,其元素取值及含义为:-1-未标号,0-已标号未检查,1-已标号已检查;


2) prev[n]为标号的第一个分量:指明标号从哪个顶点得到,以便找出可改进量;


3) alpha[n]为标号的第二个分量:用以确定增广路的可改进量α。


       另外,如前所述,从一个已标号未检查的顶点出发,对它的邻接顶点进行标号时,采用的是广度优先搜索的策略,因此,在程序中,定义了一个数组queue[n]来模拟队列;并定义了两个相关变量qs和qe,分别表示队列头位置和队列尾位置,约定从队列头取出结点,从队列尾插入结点;当qs<qe时表示队列非空。


每一次标号过程为:
(1)  先将flag、prev和alpha这3个数组各元素都初始化-1。


(2)  将源点初始化为已标号未检查顶点,即flag[0] = 0, prev[0] = 0, alpha[0] = INF,INF表示无穷大;并将源点入队列。


(3)  当队列非空并且汇点没有标号,从队列头取出队列头顶点,设这个顶点为v,v肯定是已标号未检查顶点;因此,检查顶点v的正向和反向“邻接”顶点,如果没有标号并当前可以进行标号,则对这些顶点进行标号并入队列,这些顶点都是已标号未检查顶点;此后顶点v为已标号已检查顶点。反复执行这一步直至队列为空或汇点已获得标号。

 

      标号完毕后,要进行调整,调整方法是:从汇点出发,通过标号的第1个分量,即prev[5],采用“倒向追踪”方法,一直找到源点为止,这个过程途经的顶点和弧就构成了增广路。可改进量为汇点标号的第2个分量,即alpha[5]。

 

代码:

#include <iostream>#include <cstring>#include <cmath>#include <cstring>using namespace std;#define MIN(a, b) (a > b ? b : a)const int MAXN = 1000;const int INF = 10000000;struct ArcType//每个弧的容量以及弧的实际流量{     int c, f;};ArcType Edge[MAXN][MAXN];int flag[MAXN];//如果该顶点没有标号则为-1,如果已经访问,且没有检查其邻接顶点则标记为0,如果已经访问并且已经检查其邻接顶点则标记为1;int pre[MAXN];//pre[i]表示标号时,顶点i的前一个顶点,即第一个分量int alpha[MAXN];//标号时第二个分量int que[MAXN];//广搜时的队列int v;//队列头元素出队时,接收队头元素;int qs, qe;//qs类似队列对头指针,qe为队尾指针int i, j;int n, m;//图的顶点数,边数void Ford_Fulkerson(){     while(1)     {          memset(flag, 0xff, sizeof(flag));          memset(alpha, 0xff, sizeof(alpha));          memset(pre, 0xff, sizeof(pre));          flag[0] = 0;//源点标记已经访问而未检查,并且入队,          pre[0] = 0;//一般都是把源点的前一个顶点设置为0,其第二个分量标记为无穷大,因为从源点可以流出任意多的流量          alpha[0] = INF;          qs = qe = 0;          que[qe++] = 0;          while(qs < qe && flag[n-1] == -1)//如果汇点未被标记,则进行下去          {               v = que[qs++];               for(i = 0; i < n; ++i)//检查v的邻接顶点               {                    if(flag[i] == -1)//顶点i未被访问                    {                         if(Edge[v][i].c < INF && Edge[v][i].f < Edge[v][i].c)                         {                              pre[i] = v;                              flag[i] = 0;                              alpha[i] = MIN(alpha[v], Edge[v][i].c - Edge[v][i].f);                              que[qe++] = i;                         }                         else if( Edge[i][v].c < INF && Edge[i][v].f > 0)                         {                              pre[i] = -v;                              flag[i] = 0;                              alpha[i] = MIN(alpha[v], Edge[i][v].f);                              que[qe++] = i;                         }                    }               }               flag[v] = 1;//把v标记为已经访问,并且已经检查过了          }          if(flag[n-1] == -1 || alpha[n-1] == 0)//如果汇点不能被标记,或者被标记了但是其第二个分量为0,则表示图中已经不存在增广路,已经达到最大流               break;          int k1 = n-1, k2 = fabs( pre[k1] );//倒向追踪,如果是正向弧则该弧的实际流量加上汇点的第二分量(即可增加量),如果是反向弧,则反向弧上的实际流量减去汇点的第二个分量          int a = alpha[k1];          while( 1 )          {               if(Edge[k2][k1].f < INF)                    Edge[k2][k1].f = Edge[k2][k1].f + a;               else                    Edge[k1][k2].f = Edge[k1][k2].f - a;               if(k2 == 0)                    break;               k1 = k2, k2 = fabs(pre[k2]);          }     }     int maxflow = 0;     for(i = 0; i < n; ++i)//计算最大流,并且输出各个弧的流量     {          for(j = 0; j < n; ++j)          {               if(i == 0 && Edge[i][j].f < INF)                    maxflow += Edge[i][j].f;               if(Edge[i][j].f < INF)                    cout<<i<<" -> "<<j<<":"<<Edge[i][j].f<<endl;          }     }     cout<<"MAXFLOW: "<<maxflow<<endl;}int main(){     int u, v, c, f;    while(cin>>n>>m)//n是顶点数,m是边数    {         if(n == 0 && m == 0)               break;         for(i = 0; i < n; ++i)               for(j = 0; j < n; ++j)                    Edge[i][j].c = Edge[i][j].f = INF;         for(j = 0; j < m ; ++j)         {              cin>>u>>v>>c>>f;              Edge[u][v].c = c;              Edge[u][v].f = f;         }         Ford_Fulkerson();    }    return 0;}/**6 100 1 8 20 2 4 31 3 2 21 4 2 22 1 4 22 3 1 12 4 4 03 4 6 03 5 9 34 5 7 2*/