最大流问题:最大流的Ford-Fulkerson算法

这里初学,总结出一些知识点,这里于大家共享。

引入问题:

现在想将一些物资从S运到T,必须经过一些中转站。连接中转站的是一些单向公路,每条公路都有最大运载量。最多能将多少货物从S运到T?

建图:

V表示整个图中节点的集合
E表示整个图中所有边的集合
G = (V, E)表示整个图
对于每条边(u, v), 有一个容量c(u, v)表示每条公路的最大运载量 c(u, v) ≥ 0
对于每条边(u, v), 有一个流量f(u, v)表示每条公路经过货物的数量
S表示整个图的源点,T表示整个图的汇点【s是起点,T是终点】

Ford-Fulkerson算法操作步骤:

(1)初始化网络中所有边的容量,C(u,v)称为边的容量,C(v,u)称为退回边,初始化其最大流为0;

(2)在残留网络中找到一条从源点S到汇点T的增广路P,如果找到了,转(3),否则转(5);

(3)在增广路P中找到所谓的瓶颈边【即路径中容量最小的边】,记录下这个值a,并且累加到最大流中,转(4);

(4)将增广路中所有的f(u,v)加上a,所有f(v,u)减去a,构成新的残留网络,转(2)

(5)得到最大流,退出。

从这里我们可以推测出,残余网络就是更新一条增广路经之后网络修改后当前的状态、不必要纠结其太过于深入。

那么我们如何找到增广路呢?我们可以利用宽度优先搜索来搞定这个问题:

找增广路的操作步骤:

(1)建造一个队列,建造一个pre数组建造一个vis数组【pre数组为了保存增广路径上的点的前驱顶点,vis数组为了保证不进入死循环,对计入的点进行标记】

(2)push起点S

(3)只要队列不空的条件下,取队头计cur,pop队头,遍历所有点,找没有计入过的点,并且map【cur】【i】不等于0(也就是说有流存在)

(4)如果通过这个方法找到了点T,return true,表示找到了一条增广路。

(5)如果队列空了也没找到T,return false,表示没有增广路经了。

这里对应寻找增广路的代码:

bool bfs()
{
    int i,cur;
    queueq;
    memset(pre,0,sizeof(pre));
    memset(vis,0,sizeof(vis));
    vis[s]=true;
    q.push(s);
    while(!q.empty())
    {
        cur=q.front();
        q.pop();
        if(cur==t)return true;
        for(int i=1;i<=n;i++)
        {
            if(vis[i]==0&&map[cur][i])
            {
                q.push(i);
                pre[i]=cur;
                vis[i]=true;
            }
        }
    }
    return false;
}
对应加上ford-fulkerson的代码主干:

希望大家对应ford-fulkerson的操作步骤对应理解:

int Max_Flow()
{
    int ans=0;
    while(1)
    {
        if(!bfs())return ans;
        int Min=0x3f3f3f3f;
        for(int i=t;i!=s;i=pre[i])
        {
            Min=min(Min,map[pre[i]][i]);
        }
        for(int i=t;i!=s;i=pre[i])
        {
            map[pre[i]][i]-=Min;
            map[i][pre[i]]+=Min;
        }
        ans+=Min;
    }
}


你可能感兴趣的:(算法)