poj 1273 最大流(Ford-Fulkerson算法和Dinic算法分别实现)

最大流模板题目

Ford-Fulkerson:

#include <stdio.h>
#include <string.h>
#define N 202
int q[N];//用于找增广路径的队列
int c[N][N],f[N][N];//c是路径的容量,f保存寻找过程中的流量
int a[N],p[N];//p存放一条增广路径的前一个结点
int n,m;
int maxflow(int s,int t){//从s到t的最大流
	int i,j,res = 0;
	int front,rear;
	while(1){
		front = rear = -1;
		q[++rear] = s;
		memset(a,0,sizeof(a));
		memset(p,0,sizeof(p));
		a[s] = 0x3fffffff;
		while(front < rear){
			i = q[++front];
			for(j = 1;j<=n;j++)
				if(!a[j] && c[i][j]-f[i][j]>0){
					a[j] = a[i]<c[i][j]-f[i][j]?a[i]:c[i][j]-f[i][j];
					q[++rear] = j;
					p[j] = i;
				}
		}
		if(!a[t])
			break;
		for(j = t;j!=s;j=p[j]){
			f[p[j]][j] += a[t];
			f[j][p[j]] -= a[t];
		}
		res += a[t];
	}
	return res;
}
int main(){
	freopen("a.txt","r",stdin);
	while(scanf("%d %d",&m,&n)!=EOF){
		int i,x,y,w;
		memset(c,0,sizeof(c));
		memset(f,0,sizeof(f));
		for(i = 0;i<m;i++){
			scanf("%d %d %d",&x,&y,&w);
			c[x][y] += w;
		}
		printf("%d\n",maxflow(1,n));
	}
	return 0;
}

Dinic求最大流的过程:先利用 BFS对残余网络分层,此后从源点开始,用DFS从前一层向后一层反复寻找增广路(即要求DFS的每一步都必须要走到下一层的节点)。
(因此前面在分层时,只要进行到汇点的层次数被算出即可停止,因为按照该DFS的规则,和汇点同层或更下一层的节点,是不可能走到汇点的。)
DFS过程中,要是碰到了汇点,则说明找到了一条增广路径。此时要增加总流量的值,消减路径上各边的容量,并添加反向边,即进行增广。然后进行顶点回溯,到第一条仍有容量的边的顶点处继续找增广路。如果回溯到源点而且无法继续往下走了,DFS结束。
因此,一次DFS过程中,可以找到多条增广路径。DFS结束后,对残余网络再次进行分层,重复上述步骤。

当残余网络的分层操作无法算出汇点的层次(即BFS到达不了汇点)时,算法结束,最大流求出。一般用栈实现DFS,这样就能从栈中提取出增广路径。

#include <stdio.h>
#include <string.h>
#define INF 0x3fffffff
#define min(a,b) a<b?a:b
#define N 205
int n,m;
int flow[N][N],visited[N],layer[N],q[N],stack[N];
int do_layer(int s,int t){//用队列对节点进行分层
	int i,front,rear,now;
	front = rear = -1;
	memset(layer,-1,sizeof(layer));
	q[++rear] = s;
	layer[s] = 0;
	while(front < rear){
		now = q[++front];
		for(i = s;i<=t;i++)
			if(layer[i]==-1 && flow[now][i]>0){
				layer[i] = layer[now]+1;
				if(i == t)
					return 1;
				q[++rear] = i;
			}
	}
	return 0;
}
int dinic(int s,int t){//用堆栈模拟dfs
	int i,now,top=-1,aug,res=0,temp,vs,vt;
	while(do_layer(s,t)){
		memset(visited,0,sizeof(visited));
		stack[++top] = s;
		visited[s] = 1;
		while(top>-1){
			now = stack[top];
			if(now == t){
				aug = INF;temp = 0;//temp保存回溯的位置
				for(i = 1;i<=top;i++){//找本次增广的流量
					vs = stack[i-1];
					vt = stack[i];
					if(aug>flow[vs][vt] && flow[vs][vt]>0){
						aug = min(aug,flow[vs][vt]);
						temp = vs;
					}
				}
				res += aug;
				for(i = 1;i<=top;i++){//更新流量以及反向边
					vs = stack[i-1];
					vt = stack[i];
					flow[vs][vt] -= aug;
					flow[vt][vs] += aug;
				}
				while(stack[top]!=temp){
					visited[stack[top]] = 0;
					top--;
				}
			}else{
				for(i = s;i<=t;i++)
					if(!visited[i] && flow[now][i]>0 && layer[i]==layer[now]+1){
						stack[++top] = i;
						visited[i] = 1;
						break;
					}
				if(i > t)
					top--;
			}
		}
	}
	return res;
}
int main(){
	freopen("a.txt","r",stdin);
	while(scanf("%d %d",&m,&n)!=EOF){
		int i,a,b,w;;
		memset(flow,0,sizeof(flow));
		for(i = 0;i<m;i++){
			scanf("%d %d %d",&a,&b,&w);
			flow[a][b] += w;
		}
		printf("%d\n",dinic(1,n));
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值