• 网络流--最小费用最大流 (理解)


    1、什么是最小费用最大流问题

      上篇文章我们讲解了最大流问题,那什么是最小费用最大流呢?听名字就可以看出,我们要在满足最大流的同时找到达成最大流的最小费用。

    对于一个网络流,最大流是一定的,但是组成最大流的费用是可以不同的,这里就有了在最大流网络上产生的费用流网络,就有了最小花费问题。

      简单来说,就是满足最大流的路径可能有多条,我们要从这多条路径中找到一条花费代价最小的路径。所以最大流是解决这类问题的前提

    2、EK算法 + SPFA 最短路

        我们用每条边单位流量的花费作为边权,假如一条合法路径上每条边的花费分别为 c1,c2,.......ck , 并且这条边上的最小流量为flow,

      那么这条路径上的花费为 : c1 * flow + c2*flow + ..... + ck*flow = (c1+ c2 + c3 + .... + ck)*flow =  dis [ci] * flow

      这里的 dis[ci] 就是我们要求的最短路!

    3、算法思想 

      采用贪心的思想,每次找到一条从源点到达汇点的路径,增加流量,且该条路径满足使得增加的流量的花费最小,直到无法找到一条从源点到达汇点的路径,算法结束。
    由于最大流量有限,每执行一次循环流量都会增加,因此该算法肯定会结束,且同时流量也必定会达到网络的最大流量;同时由于每次都是增加的最小的花 费,即当前的最小花费是所有到达当前流量flow时的花费最小值,因此最后的总花费最小。


    4、求解步骤

      1、找到一条从源点到达汇点的花费最小的路径,该花费 = 使用该路径上的边的单位费用之和
      2、然后找出这条路径上的边的容量的最小值f,则当前最大流 max_flow 扩充f(求最大流的过程,同时当前最小费用 min_cost 扩充 f*min_dist(s,t)
      3、更新路径流量 flow[][] ,将这条路径上的每条正向边的容量都减少f,每条反向边的容量都增加f。
      4、重复(1~3)直到无法找到从源点到达汇点的路径。
     

    5、需要注意几点

      1、注意超级源点和超级终点的建立。这里的超级源点/汇点的建立是把所有边统一起来,构成一张网络图,因为有些题目不会直接给你网络图,

      需要你自己去建立,例如 hdu 1533

      2、初始化时,正向边的单位流量费用为cost[u][v],那么反向边的单位流量费用就为 -cost[u][v]。正向边和反向边的费用互为相反数,因为回流费用减少。
      3、费用cost数组和容量cap数组每次都要初始化为0。
      4、求解从源点到汇点的“最短”路径时,由于网络中存在负权边,因此使用SPFA来实现。

    6、代码

      

    int cap[500][500],cost[500][500],flow[500][500];//cap是容量,cost是花费,flow是流量
    int pre[500],dis[500],vis[500],cnt[500];//pre是记录增广路的前驱节点,dis[i]是记录源点到节点i的最小距离
    //vis[i]标记点是否在队列中,cnt[i]记录点i进入队列的次数
    
    int n,m;
    int st,endd;
    int mn_cost,mx_flow;
    int spfa()
    {
        for(int i=0;i<n;i++)
            dis[i]=mx;
        memset(vis,0,sizeof(vis));
        memset(cnt,0,sizeof(cnt));
        
        queue<int>p;
        p.push(st);//st是起点
        vis[st]=1;
        cnt[st]=1;
        dis[st]=0;
        while(!p.empty())
        {
            int now=p.front();
            p.pop();
            vis[now]=0;
            for(int i=0;i<n;i++)
            {
                if(cap[now][i]>flow[now][i]&&dis[i]>dis[now]+cost[now][i])//这里将费用看成是长度,求源点到汇点的最小距离
                {
                    dis[i]=dis[now]+cost[now][i];
                    pre[i]=now;
                    if(vis[i]==0)
                    {
                        p.push(i);
                        vis[i]=1;
                        cnt[i]++;
                        if(cnt[i]>n)
                            return 0;
                    }
                }
            }
        }
        if(dis[endd]>=mx)
            return 0;
        return 1;
    }
    void bfs(int n)//顶点数
    {
        memset(flow,0,sizeof(flow));
        mx_flow=0;
        mn_cost=0;
        while(spfa())
        {
            int f=mx;
            for(int i=endd;i!=st;i=pre[i])
                f=min(f,cap[pre[i]][i]-flow[pre[i]][i]);
    
            for(int i=endd;i!=st;i=pre[i])
            {
                flow[pre[i]][i]+=f;
                flow[i][pre[i]]-=f;
            }
            mn_cost+=dis[endd]*f;        
         mx_flow+=f; } }
    模板题:https://www.cnblogs.com/-citywall123/p/11329609.html

  • 相关阅读:
    面试官:你对Redis缓存了解吗?面对这11道面试题你是否有很多问号?
    2020年Java多线程与并发系列22道高频面试题(附思维导图和答案解析)
    Java开发5年,四面美团(多线程+redis+JVM+数据库),终拿offer!
    从阿里、腾讯的面试真题中总结了这11个Redis高频面试题
    2020年Java基础高频面试题汇总(1.4W字详细解析)
    2020年大厂Java面试前复习的正确姿势(800+面试题附答案解析)
    吃透这份pdf,面试阿里、腾讯、百度等一线大厂,顺利拿下心仪offer!
    3月编程语言排行及程序员工资,快看看你在哪个等级
    2020年春招面试必备Spring系列面试题129道(附答案解析)
    51单片机putchar函数的说明
  • 原文地址:https://www.cnblogs.com/-citywall123/p/11329508.html
Copyright © 2020-2023  润新知