网络流 FF算法简析

Ford-Fulkerson(FF)算法是一种解决网络流问题中的最大流问题的增广路算法。它通过寻找增广路径并调整边的流量来逐步接近最优解。虽然存在效率低下的问题,但在理解最大流算法原理时具有一定的价值。FF算法引入反向边以允许回溯,从而可能找到最优解。尽管通常不作为首选算法,但了解其工作原理有助于学习更高效的算法如Dinic算法。

FFFFFFFord−FulkersonFord-FulkersonFordFulkerson)算法,用于求从源点到汇点的最大可行流(以下简称最大流)问题的算法。

求解最大流问题的算法很多,FF是最拉胯的那一档()。

属于增广路类算法,主要思想就是通过增广路对于现有网络流不断改进求出最优。

f[e]f[e]f[e]表示边的流量,c[e]c[e]c[e]表示容量,对于一个可行流必有f[e]<=c[e]f[e]<=c[e]f[e]<=c[e](不然就溢出了)

首先我们很容易想出一个正确性未知的算法:

每次从sssttt的路径中选出一条所有边上的f[e]f[e]f[e]<c[e]<c[e]<c[e],那么这条边上一定可以再多一些流量,答案就可以加上最小的c[e]−f[e]c[e]-f[e]c[e]f[e],且每条边的流量加上这个值。

但是这个在实际操作中就会发现:有些不对啊~

举个例子:
请添加图片描述
然而这张图的最大流可以达到11。

如果我们在某些决策上可以进行返回那么是不是就可以保证答案最优了呢?
在上述例子中,如果将1—21—21—2的流退回去,得到新的流:s—2—1—3—ts—2—1—3—ts—2—1—3—t,就可以得到最优结果。

所以我们新加入一类边:反向边。对正向边的操作反向操作在反向边上。

改进操作就是在寻找路径时不仅局限于f[e]<c[e]f[e]<c[e]f[e]<c[e],还可以加入反向边的f[e]>0f[e]>0f[e]>0的边起到反悔的作用。

这就是FF算法了。

请添加图片描述

不过因为该算法缺点明显所以很少用。

以下为模板代码:

#include<bits/stdc++.h>
typedef long long ll;
struct node{
	int y,cap,fid;
};
std::vector<node> edge[201001];
bool vv[200101];
void add(int xx,int yy,int cc){
	edge[xx].push_back((node){yy,cc,edge[yy].size()});
	edge[yy].push_back((node){xx,0,edge[xx].size()-1});
}
int dfs(int v,int t,int f){
	if(v==t) return f;
	vv[v]=1;
	for(int i=0;i<(int)edge[v].size();i++){
		node &e=edge[v][i];
		if(!vv[e.y]&&e.cap>0){
			int d=dfs(e.y,t,std::min(f,e.cap));
			if(d>0){
				e.cap-=d;
				edge[e.y][e.fid].cap+=d;
				return d;
			}
		}
	}
	return 0;
}
ll solve(int s,int t){
	ll floww=0;
	while(1){
		memset(vv,0,sizeof(vv));
		int f=dfs(s,t,1e9);
		if(f==0) return floww;
		floww+=f;
	}
}
int n,m,s,t;
int main(){
	scanf("%d%d%d%d",&n,&m,&s,&t);
	for(int i=1;i<=m;i++){
		int u,v,c;
		scanf("%d%d%d",&u,&v,&c);
		add(u,v,c);
	}
	printf("%lld",solve(s,t));
	return 0;
}

如果觉得FFFFFF算法过于拉胯也可以学习一下DinicDinicDinic算法:

先贴个代码():

#include<bits/stdc++.h>
typedef long long ll;
struct node{
	int y,cap,fid;
};
std::vector<node> edge[201001];
int dep[201001];
int iter[201001];
void add(int xx,int yy,int zz) {
    edge[xx].push_back((node){yy,zz,edge[yy].size()});
    edge[yy].push_back((node){xx,0,edge[xx].size()-1});
}
void bfs(int u){
    memset(dep,-1,sizeof(dep));
    std::queue<int> q;
    dep[u]=0;
    q.push(u);
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=0;i<edge[x].size();i++){
            node &e=edge[x][i];
            if (e.cap>0&&dep[e.y]<0){
                dep[e.y]=dep[x]+1;
                q.push(e.y);
            }
        }
    }
}
int dfs(int v,int t,int f){
    if(v==t) return f;
    for(int&i=iter[v];i<edge[v].size();i++){
        node &e=edge[v][i];
        if(e.cap>0&&dep[v]<dep[e.y]){
            int d=dfs(e.y,t,std::min(f,e.cap));
            if(d>0){
                e.cap-=d;
                edge[e.y][e.fid].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
ll solve(int s,int t){
    ll flow=0;
    while(1){
        bfs(s);
        if(dep[t]<0) return flow;
        memset(iter,0,sizeof(iter));
        int f;
        while((f=dfs(s,t,1e9))>0){
            flow+=f;
        }
    }
}
int n,m,s,t;
int main(){
	scanf("%d%d%d%d",&n,&m,&s,&t);
	for(int i=1;i<=m;i++){
		int u,v,c;
		scanf("%d%d%d",&u,&v,&c);
		add(u,v,c);
	}
	printf("%lld",solve(s,t));
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值