对EK的一些理解

首先是网络流问题的提出:
一个源点,一个汇点,不断又源点向汇点输送,其中路径上有最大速度,求汇点收货的最大速度是多少。
在求之前可以先模拟一下过程如果源点现在只能出一个单位的货,那么他会找一个未达到最大速度的路进行运输,接着我们不断将运输的量放大,即不断地从起点开始寻找增广路,每次都对其进行增广,直到源点和汇点不连通,也就是找不到增广路为止。可知当找不到增广路的时候,当前的流量就是最大流
那么像这种先扩张再找终点的情况可以用bfs来实现,即将点存在队列里。
但这样找到的只是先从这条路开始找所找到的最大流,故应该是他在发现不是最大时具备回溯的能力,但回溯搜索会使我们的效率就上升到指数级,因此我们引入反向边即每条边(i,j)都有一条反向边(j,i),反向边也同样有它的值,在把路上每一段的容量减少delta的同时,也把每一段上的反方向的容量增加delta。
这样方法就有了,就差构建了
变量定义

#define maxn 201
int maxData = 0x7fffffff;
int capacity[maxn][maxn]; //记录残留网络的值
int flow[maxn];//标记从源点到当前节点实际还剩多少流量可用
int pre[maxn];//标记在这条路径上当前节点的前驱,同时标记该节点是否在队列中
queue<int> ekqueue;

BFS搜索
通过搜索找出增广路

int BFS(int src,int des)//bfs用来找由src到des的最大值
{
    int i,j;
    while(!ekqueue.empty())//初始化队列为空
        ekqueue.pop();
    for(i=1;i<m+1;++i)//将前驱全部初始为-1
    {
        pre[i]=-1;
    }
    pre[src]=0;
    flow[src]= maxData;//源点的值是无限大
    ekqueue.push(src);//将源点入队
    while(!ekqueue.empty())//BFS一直搜索到没有扩张点
    {
        int index = ekqueue.front();//将第一个点提到index
        ekqueue.pop();//第一个点出队
        if(index == des)//找到了增广路径
            break;
        for(i=1;i<m+1;++i)
        {
            if(i!=src && capacity[index][i]>0 && pre[i]==-1)//存在剩余网络且那个点不在队列中

            {
                 pre[i] = index; //记录前驱
                 flow[i] = min(capacity[index][i],flow[index]);//关键:迭代的找到增量
                 ekqueue.push(i);
            }
        }
    }
    if(pre[des]==-1)//残留图中不再存在增广路径,即找到了一个最大流
        return -1;
    else
        return flow[des];
}

接着我们将增广后的增量添加到总价值里

int maxFlow(int src,int des)//最大流
{
    int increasement= 0;//增量
    int sumflow = 0;//最终的最大流
    while((increasement=BFS(src,des))!=-1)
    {
         int k = des; //利用前驱寻找路径
         while(k!=src)
         {
              int last = pre[k];
              capacity[last][k] -= increasement; //改变正向边的容量
              capacity[k][last] += increasement; //改变反向边的容量
              k = last;
         }
         sumflow += increasement;
    }
    return sumflow;
}

这样看可能还是不大好懂那么我们以一组样例为例:
“ 5 4 ”
“ 1 4 20 ”
“ 1 2 40 ”
“ 2 4 20 ”
” 2 3 30 ”
” 3 4 10 ”
对第一条边:
1. flow[1]=INF,pre[1]=0;

    源点1进队列,开始找增广路,
    capacity[1][2]=40>0,则flow[2]=min(flow[1],40)=40;

    capacity[1][4]=20>0,则flow[4]=min(flow[1],20)=20;

    capacity[2][3]=30>0,则flow[3]=min(folw[2]=40,30)=30;

    capacity[2][4]=30,但是pre[4]=1(已经在capacity[1][4]这遍历过4号点了)

    capacity[3][4].....

    当index=4(汇点),结束增广路的寻找

    传递回increasement(该路径的流),利用前驱pre寻找路径

这时由1到4出现了一条零路,由4到1的价值为20(反向边)
2.flow[1]=INF,pre[1]=0;

源点1进队列,开始找增广路,capacity[1][2]=40>0,则flow[2]=min(flow[1],40)=40;

    capacity[1][4]=0!>0,跳过

    capacity[2][3]=30>0,则flow[3]=min(folw[2]=40,30)=30;

    capacity[2][4]=30,pre[4]=2,
    则flow[2][4]=min(flow[2]=40,20)=20;

    capacity[3][4].....

    当index=4(汇点),结束增广路的寻找

    传递回increasement(该路径的流),利用前驱pre寻找路径

一直这样搜下去
最终sumflow=20+20+10=50(这个就是最大流的值)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值