• 最大流——EK算法


    一、算法理论

    【基本思想】

    反复寻找源点s到汇点t之间的增广路径,若有,找出增广路径上每一段[容量-流量]的最小值delta,若无,则结束。
    在寻找增广路径时,可以用BFS来找,并且更新残留网络的值(涉及到反向弧)。
    而找到delta后,则使最大流值加上delta,更新为当前的最大流值。

    【算法详解】

    这么一个图,求源点1到汇点4的最大流。

    由于我是通过模版真正理解ek的含义,所以先上代码,通过分析代码,来详细叙述ek算法。

    #include <iostream>
    #include <queue>
    #include<string.h>
    using namespace std;
    #define arraysize 201
    int maxData = 0x7fffffff;
    int capacity[arraysize][arraysize]; //记录残留网络的容量
    int flow[arraysize];                //标记从源点到当前节点实际还剩多少流量可用
    int pre[arraysize];                 //标记在这条路径上当前节点的前驱,同时标记该节点是否在队列中
    int n,m;
    queue<int> myqueue;
    int BFS(int src,int des)
    {
        int i,j;
        while(!myqueue.empty())       //队列清空
            myqueue.pop();
        for(i=1;i<m+1;++i)
        {
            pre[i]=-1;
        }
        pre[src]=0;
        flow[src]= maxData;
        myqueue.push(src);
        while(!myqueue.empty())
        {
            int index = myqueue.front();
            myqueue.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]);   //关键:迭代的找到增量
                     myqueue.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;
    }
    int main()
    {
        int i,j;
        int start,end,ci;
        while(cin>>n>>m)
        {
            memset(capacity,0,sizeof(capacity));
            memset(flow,0,sizeof(flow));
            for(i=0;i<n;++i)
            {
                cin>>start>>end>>ci;
                if(start == end)               //考虑起点终点相同的情况
                   continue;
                capacity[start][end] +=ci;     //此处注意可能出现多条同一起点终点的情况
            }
            cout<<maxFlow(1,m)<<endl;
        }
        return 0;
    }
    

    显而易见capacity存变的流量,进行ek求解。

    对于BFS找增广路:

    • 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寻找路径

    路径也自然变成了这样:

    • 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(这个就是最大流的值)

    二、算法分析

    • 时间复杂度为O(m2n)
    • 而接下来的Dinic算法的时间复杂度为O(n2m)
  • 相关阅读:
    PHP查找服务器某个目录下的文件
    CentOS添加用户,管理员权限
    centos图形化桌面安装过程
    正则表达式匹配查询
    Windows远程桌面多用户登录的问题
    VM虚拟机Android安装图形界面
    Ubuntu 根目录作用
    win7个性化不能换界面:此页面上的一个或多个设置已被系统管理员禁用,关机里的切换用户和锁定为灰色
    springboot 使用 jedis 连接 Redis 数据库
    JAVA 中 Map 与实体类相互转换的简单方法
  • 原文地址:https://www.cnblogs.com/xzxl/p/7236154.html
Copyright © 2020-2023  润新知