算法实践课留了自学算法。看了一些资料。
原文链接:https://www.cnblogs.com/kuangbin/archive/2011/07/26/2117636.html?tdsourcetag=s_pctim_aiomsg
原文链接:https://blog.csdn.net/Ivan_zgj/article/details/51580993
现在自己整理一下。
前提引入
1962 年L.R.Ford和D.R.Fulkerson把原始-对偶算法应用于最大流问题,提出最大流问题的标号算法。简称FF算法,下面引入相关概念。
定义
设容量网络N=<V,E,c,s,t>,f是N上的一个可行流。N中流量等于容量的边称作饱和边,流量小于容量的边称作非饱和边,流量等于0的边称作零流边,流量大于0的边称为非零流边。
不考虑边的方向,N中从顶点到i到j的一条边不重复的路径称为i-j链,i-j链的方向是从i到j,链中与链的方向的一致的边称作为前向边,与链的方向相反的边称作后向边,如果链中所有前向边都是非饱和的,所有后向边都是非零流的,则称这条链为i-j增广链。
Ford-Fulkerson算法
Ford-Fulkerson算法为迭代算法,基本思路如下:
首先,对所有结点i,j∈V令f(i,j)=0;
然后,通过迭代方式不断在残留网络中寻找一个增广路径来增加流值,直到残留网络中不包括增广路径为止。
具体地说:
首先,初始化一条容量为0的流f和一个残留网络Nf, 第一个残留网络为原图N,每条边的残留容量初始化为每条边的初始容量: cf(i,j)= c(i,j)。
然后,在残留网络Nf中寻找增广路径P,取增广路径P中的边的残留容量cf(i,j)最小值作为流的增量△f,使得: f’=f+ △ f
最后,修改剩余图中每条边的容量: c’f(i,j)=cf(i,j)- △ f 得到残留网络Nf.
重复上述过程,直到找不到一条增广路径为止
步骤
Ford-Fulkerson方法是一种迭代的计算过程,实现步骤如下:
1.初始化图中流的值;
2.在残存网络中找到增广路径;
3.沿着增广路径增加流的值,直到残存网络不存在增广路径为止
例题
对图中的容量网络应用FF算法,图中边旁的数是容量。
用FF算法的计算过程如下图所示。(同教材175页)
算法
Ford-Fulkerson算法
Ford-Fulkerson方法的伪代码如下。其中<u,v>代表顶点u到顶点v的一条边,<u,v>.f表示该边的流量,c是边容量矩阵,c(i,j)表示边<i,j>的容量,当边<i,j>不存在时,c(i,j)=0。e为残存网络矩阵,e(i,j)表示边<i,j>的值,当边<i,j>不存在时,e(i,j)=0。E表示边的集合。f表示流网络。Ford-Fulkerson方法的伪代码如下。其中<u,v>代表顶点u到顶点v的一条边,<u,v>.f表示该边的流量,c是边容量矩阵,c(i,j)表示边<i,j>的容量,当边<i,j>不存在时,c(i,j)=0。e为残存网络矩阵,e(i,j)表示边<i,j>的值,当边<i,j>不存在时,e(i,j)=0。E表示边的集合。f表示流网络。
Ford-Fulkerson
for <u,v> ∈ E
<u,v>.f = 0
while find a route from s to t in e
m = min(<u,v>.f, <u,v> ∈ route)
for <u,v> ∈ route
if <u,v> ∈ f
<u,v>.f = <u,v>.f + m
else
<v,u>.f = <v,u>.f - m
原文链接:https://blog.csdn.net/Ivan_zgj/article/details/51580993
原文链接:https://www.cnblogs.com/kuangbin/archive/2011/07/26/2117636.html?tdsourcetag=s_pctim_aiomsg
最后再放上两个原文链接供学习使用。
代码:
#include<iostream>
#include<queue>
using namespace std;
const int maxn=205;//最大结点数
const int inf=0x7fffffff;
int r[maxn][maxn]; //残留网络,初始化为原图
bool visit[maxn];//是否被访问过
int pre[maxn];//前驱子图,记录了从顶点s到终点的一条可行路径上的其它顶点的前一个顶点,从前驱子图中找到从s到t的一条完整路径
int m,n;//边数,顶点数
bool bfs(int s,int t) //广度优先寻找一条从s到t的增广路,若找到返回true
{
int p;
queue<int> q;//创建一个队列的对象
memset(pre,-1,sizeof(pre));//初始化,把数组pre中所有元素设置为-1
memset(visit,false,sizeof(visit));
pre[s]=s;
visit[s]=true;
q.push(s);//把源点放进队列里面
while(!q.empty())//当队列不为空,继续找下一个结点
{
p=q.front();//取出队首
q.pop();
for(int i=1;i<=n;i++)
{
if(r[p][i]>0&&!visit[i])//如果从p出发到其他顶点,有残留容量大于0,并且没有被标记
{
pre[i]=p;//记录通路
visit[i]=true;
if(i==t) //如果找到汇点就结束
return true;
q.push(i);//接着将该点进栈
}
}
}
return false;
}
int EK(int s,int t)//计算最大流
{
int flow=0,d,i;
while(bfs(s,t))//当可以找到时
{
d=inf;
for(i=t;i!=s;i=pre[i])
d=d<r[pre[i]][i]? d:r[pre[i]][i];//找到增广路径中最小流量,如果d<r[pre[i]][i],把d的值赋给d,否则把r[pre[i]][i]的值赋给d
for(i=t;i!=s;i=pre[i])//顺藤摸瓜找回去,正向,逆向分别更改
{
r[pre[i]][i]-=d;//正
r[i][pre[i]]+=d;//反
}
flow+=d;//
}
return flow;
}
int main()
{
while(scanf("%d%d",&m,&n)!=EOF)
{
int u,v,w;
memset(r,0,sizeof(r));//
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&u,&v,&w);
r[u][v]+=w;
}
printf("最大流:");
printf("%d\n",EK(1,n));
}
return 0;
}