网络最大流之一般增广路方法------Ford-Fulkerson

      在该算法中,寻找增广路和改进网络流的方法称为标记法。

       对于标记的过程不多加阐述,以下对标记的程序实现做下小小总结:

1:在程序中需要定义三个数组变量分别是flag[],prev[],alpha[],其中:

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

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

 alpha[]为标号的第二个分量,用以确定增广路的可改进量

2:每一次的标号过程如下

(1)先将flag,prev,alpha这3个数组初始化为-1

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

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

3:

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

 

参考模版:

#include<stdio.h>
#include<string.h>
#include<math.h>
#define MAX 1000
#define INF 100000
#define MIN(a,b) ((a)<(b) ? (a):(b))
struct map//弧结构
{
	int c,f;//容量,流量
}edge[MAX][MAX];
int m,n;//弧数 顶点数
int flag[MAX]; //顶点的状态,-1未标号,0已标号未检查,1已标号已检查
int pre[MAX]; //标号的第一个分量:指明标号从哪个顶点得到,以便找出改进量
int alpha[MAX]; //标号的第二个分量,可改进量β
int queue[MAX]; //模拟队列
int qs,qe; //队列头和队列尾位置
int v; //从队列里取出来的队头元素
int i,j;
//寻找增广路
void BFS(){
	memset(flag, 0xff, sizeof(flag)); //初始化为-1
	memset(pre, 0xff, sizeof(pre));
	memset(alpha, 0xff, sizeof(alpha));
	flag[0] = 0; //源点初始化,已标号未检查
	pre[0] = 0;
	alpha[0] = INF;
	qs = qe = 0;
	queue[qe] = 0; //源点入队列
	qe++;
	while(qs<qe && flag[n-1]==-1)
	{
		v= queue[qs]; //取出队列头顶点
		qs++;
		for(i=0; i<n; i++)//检查顶点v的正向和反向邻接点
		{ 
			if(flag[i] == -1)//顶点未标号
			{ 
				if(edge[v][i].c<INF && edge[v][i].f<edge[v][i].c)//正向且未满
				{ 
					flag[i] = 0; //给顶点i标号(已标号未检查)
					pre[i] = v;
					alpha[i] = MIN(alpha[v],edge[v][i].c-edge[v][i].f);
					queue[qe] = i; //顶点i入队列
					qe ++;
				}
				else if(edge[i][v].c<INF && edge[i][v].f>0) //反向且有流量
				{
					flag[i] = 0; //给顶点i标号(已标号未检查)
					pre[i] = -v;
					alpha[i] = MIN(alpha[v], edge[i][v].f);
					queue[qe] = i; //顶点i入队列
					qe ++;
				}
			}
		}
		flag[v] = 1; //顶点i已标号已检查
	}
	
}
//Ford_Fulkerson算法
void Ford_Fulkerson()
{ 
	while(1)
	{
		BFS();
		if( alpha[n-1]==0 || flag[n-1]==-1 ) 
			//当汇点没有获得标号,或者汇点调整量为0, 应该退出循环
			break;
		//当汇点有标号,应该进行调整
		int k1=n-1, k2=abs( pre[k1] );
		int a = alpha[n-1]; //可改进量
		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) //调整一直到源点v0
				break;
			k1 = k2;
			k2 = abs( pre[k2] );
		}
		alpha[n-1] = 0;
	}
	
	
}
//求最大流
void Max_flow()
{
	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)
				printf("%d->%d:%d\n",i,j,edge[i][j].f);
		}
	}
	printf("maxflow: %d\n",maxflow);
}
int main()
{
	int u,v,c,f;
	scanf("%d%d",&n,&m);
	for(i=0; i<n; i++)
		for(j=0; j<n; j++)
			edge[i][j].c = edge[i][j].f = INF;
		for(i=0; i<m; i++) //读入每条弧
		{
			scanf("%d%d%d%d",&u,&v,&c,&f);
			edge[u][v].c = c; //构造邻接矩阵
			edge[u][v].f = f;
		}
		Ford_Fulkerson();
		Max_flow();
		return 0;
}


 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值