网络最大流求解 增广路算法

增广路算法:

根据增广路地理,为了得到最大流,可以从任何一个可行流开始,沿着增广路对网络流进行增广,直到网络中不存在增广路为止,这样的算法称为增广路算法。

增广路算法流程如下。

(1)取一个可行流f作为初始流(如果没有给定可行流,则取零流作为初始流)。

(2)寻找关于f的增广路P,如果找到,则沿着这条增广路P将f改进成一个更大的流。

(3)重复 第(2)步直到找不到增广路为止。

增广路算法的关键是寻找增广路和改进网络流。

主要有三种增广路算法。(1)Ford_Fulkerson算法。(2)最短增广路算法。(3)Dinic算法(连续增广路算法)。后两种算法效率更高。


Ford_Fulkerson算法:

#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cctype>
#include <cmath>
#include <stack>
#include <queue>
#include <list>
#include <map>
#include <set>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int maxn = 1000;
const int inf = 10000000;

struct arc
{
	int c,f;//容量、流量 
};

int n,m;
arc edge[maxn][maxn];
int flag[maxn];		//顶点状态 -1为标号 0已标号未检查 1已标号已检查 
int prev[maxn];		//标号的第1个分量 
int al[maxn];		//标号的第2个分量
 


void printFlow()
{
	int maxFlow = 0;
	for(int i = 0; i < n; i++)
	{
		for(int 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);		
}

void input()
{
	int u,v,c,f;
	scanf("%d%d",&n,&m);
	for(int i = 0; i < n; i++)
	{
		for(int j = 0; j < n; j++)
			edge[i][j].c = edge[i][j].f = inf;
	}
	
	for(int i = 0; i < m; i++)
	{
		scanf("%d%d%d%d",&u,&v,&c,&f);
		edge[u][v].c = c;
		edge[u][v].f = f;
	}	
}

void ford_fulkerson()
{
	while(1)//直至标号不存在 
	{
		queue<int> q;
		memset(flag,-1,sizeof(flag));
		memset(prev,0,sizeof(prev));
		memset(al,0,sizeof(al));
		flag[0] = 0; prev[0] = 0; al[0] = inf;
		q.push(0);
		
		//队列非空并且汇点未标号 
		while(!q.empty() && flag[n-1] == -1) 
		{
			int v = q.front(); q.pop();
			for(int i = 0; i < n; i++)
			{
				if(flag[i] == -1)//该顶点未标号 
				{
					//正向且流量未满
					if(edge[v][i].c != inf && edge[v][i].f < edge[v][i].c)
					{
						//标号 
						flag[i] = 0; prev[i] = v;
						al[i] = min(al[v],edge[v][i].c - edge[v][i].f);
						q.push(i);
					}
					//反向且有流量
					else if(edge[i][v].c != inf && edge[i][v].f > 0) 
					{
						flag[i] = 0; prev[i] = -v;
						al[i] = min(al[v],edge[i][v].f);
						q.push(i);
					}
				}
				flag[v] = 1;//该顶点已检查 
			}
			
		}//end of while
		
		//汇点未标号或者调整量为0 
		if(flag[n-1] == -1 || al[n-1] == 0) break;
		int p1 = n-1,p2 = abs(prev[p1]);
		int a = al[n-1];
		
		while(1)
		{
			if(edge[p2][p1].f != inf) 
				edge[p2][p1].f += a;
			else 
				edge[p1][p2].f -= a;
			
			if(p2 == 0) break;
			p1 = p2;
			p2 = abs(prev[p1]);
		};
		
	}//end of while(1)
	
	//最大流 
	printFlow();
	
}//end of ford

int main()
{
	input();
	//初始流 
	printFlow();
	ford_fulkerson(); 
	return 0;
}

/*

 input:
 6 10
0 1 8 2
0 2 4 3
1 3 2 2
1 4 2 2
2 1 4 2
2 3 1 1
2 4 4 0
3 4 6 0
3 5 9 3
4 5 7 2

output:
0->1:2
0->2:3
1->3:2
1->4:2
2->1:2
2->3:1
2->4:0
3->4:0
3->5:3
4->5:2
maxFlow:5
0->1:4
0->2:4
1->3:2
1->4:2
2->1:0
2->3:1
2->4:3
3->4:0
3->5:3
4->5:5
maxFlow:8

*/

下面两个是两个改版Ford_Fulkerson算法,分别用dfs和bfs来寻找增广路:

bfs:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>


using namespace std;

const int maxn = 1005;
const int inf = 100000;


struct Arc
{
  int c,f;//容量、流量 
};


int n,m; 
Arc edge[maxn][maxn];
int vis[maxn];
int a[maxn];
int p[maxn];



void input()
{
  int u,v,c,f;
  scanf("%d%d",&n,&m);
  
  for(int i = 0; i < n; i++){
    for(int j = 0; j < n; j++){
      edge[i][j].c = edge[i][j].f = inf;
    }
  }
  
  for(int i = 0; i < m; i++){
    scanf("%d%d%d%d",&u,&v,&c,&f);
    edge[u][v].c = c;
    edge[u][v].f = f;
  }
  
}


//寻找增广路 
int bfs()
{
  queue<int>  q;
  memset(vis,0,sizeof(vis));
  q.push(0);
  vis[0] = 1;
  a[0] = inf;
  
  //找到一条增广路 
  while(!q.empty()){
    int u = q.front(); q.pop();
    
    for(int i = 0; i < n; i++){
      if(!vis[i]){
        //正向且未满 
        if(edge[u][i].c != inf && edge[u][i].f < edge[u][i].c){
          vis[i] = 1; p[i] = u;
          a[i] = min(a[u],edge[u][i].c-edge[u][i].f);
          q.push(i);
        }
        //反向且有流量
        else if(edge[i][u].c != inf && edge[i][u].f > 0){
          vis[i] = 1; p[i] = -u;
          a[i] = min(a[u],edge[i][u].f);
          q.push(i);
        }
      }
    }
    
    vis[u] = 1;
    if(vis[n-1]) break;
  }
  
  
  if(!vis[n-1]) return 0;
  
  //增广
  int t = n-1;
  int alpha = a[n-1];
  while(t != 0){
    
    if(p[t] >= 0){//正向 
      edge[p[t]][t].f += alpha;
    }
    else{//反向 
      edge[t][-p[t]].f -= alpha;
    }
    t = abs(p[t]);
  }
  
  return a[n-1];
}

void printFlow()  
{  
    int maxFlow = 0;  
    for(int i = 0; i < n; i++)  
    {  
        for(int 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()
{
  input();
  printFlow();
  while(bfs());
  printFlow();
  return 0;
}

/* 
 
 input: 
 6 10 
0 1 8 2 
0 2 4 3 
1 3 2 2 
1 4 2 2 
2 1 4 2 
2 3 1 1 
2 4 4 0 
3 4 6 0 
3 5 9 3 
4 5 7 2 
 
output: 
0->1:2 
0->2:3 
1->3:2 
1->4:2 
2->1:2 
2->3:1 
2->4:0 
3->4:0 
3->5:3 
4->5:2 
maxFlow:5 
0->1:4 
0->2:4 
1->3:2 
1->4:2 
2->1:0 
2->3:1 
2->4:3 
3->4:0 
3->5:3 
4->5:5 
maxFlow:8 
 
*/  

dfs:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>


using namespace std;

const int maxn = 1005;
const int inf = 100000;


struct Arc
{
  int c,f;//容量、流量 
};


int n,m; 
Arc edge[maxn][maxn];
int vis[maxn];

void input()
{
  int u,v,c,f;
  scanf("%d%d",&n,&m);
  
  for(int i = 0; i < n; i++){
    for(int j = 0; j < n; j++){
      edge[i][j].c = edge[i][j].f = inf;
    }
  }
  
  for(int i = 0; i < m; i++){
    scanf("%d%d%d%d",&u,&v,&c,&f);
    edge[u][v].c = c;
    edge[u][v].f = f;
  }
  
}

int dfs(int u, int alpha)
{
  
  if(u == n-1) return alpha;
  if(vis[u]) return 0;
  
  vis[u] = 1;
  for(int i = 0; i < n; i++){
    if(!vis[i]){
      int flow;
      //正向且未满 
      if(edge[u][i].c != inf && edge[u][i].f < edge[u][i].c){
        flow = dfs(i,min(alpha,edge[u][i].c-edge[u][i].f));
        edge[u][i].f += flow;
    
        if(flow != 0) return flow;
      }
      //反向且有流量 
      else if(edge[i][u].c != inf && edge[i][u].f > 0){
        flow = dfs(i,min(alpha,edge[i][u].f));
        edge[i][u].f -= flow;
        
        if(flow != 0) return flow;
      }
    }
  }
  return 0;
}

void printFlow()  
{  
    int maxFlow = 0;  
    for(int i = 0; i < n; i++)  
    {  
        for(int 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()
{
  input();
  printFlow();
  while(dfs(0,inf)){
    //printFlow();
    memset(vis,0,sizeof(vis));
  }
  printFlow();
  return 0;
}

/* 
 
 input: 
 6 10 
0 1 8 2 
0 2 4 3 
1 3 2 2 
1 4 2 2 
2 1 4 2 
2 3 1 1 
2 4 4 0 
3 4 6 0 
3 5 9 3 
4 5 7 2 
 
output: 
0->1:2 
0->2:3 
1->3:2 
1->4:2 
2->1:2 
2->3:1 
2->4:0 
3->4:0 
3->5:3 
4->5:2 
maxFlow:5 
0->1:4 
0->2:4 
1->3:2 
1->4:2  
2->1:0 
2->3:1 
2->4:3 
3->4:0 
3->5:3 
4->5:5 
maxFlow:8 
 
*/  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值