• 网络最大流(EK)


    以前在oi中见到网络流的题都是直接跳过,由于本蒟蒻的理解能力太弱,导致网络流的学习不断推迟甚至被安排在了tarjan之后,原本计划于学习完最短路后就来学网络流的想法也随之破灭,在看完众多大佬

    的博客后,我发现我不怎么能看懂(因为我自己太菜了),所以特来写一篇整理一下自己所学到的.

    常见的网络流算法根据优化程度有FF<EK<Dinic<ISAP,由于后两种算法比较复杂,我至今也没有很好的理解,今天只要是我自己的一些对EK的理解.

    首先需要了解一下什么是网络最大流:

    网络流:所有弧上流量的集合f={f(u,v)},称为该容量网络的一个网络流.

    • 定义:带权的有向图G=(V,E),满足以下条件,则称为网络流图(flow network):

      1. 仅有一个入度为0的顶点s,称s为源点
      2. 仅有一个出度为0的顶点t,称t为汇点
      3. 每条边的权值都为非负数,称为该边的容量,记作c(i,j)。

      弧的流量:通过容量网络G中每条弧< u,v>,上的实际流量(简称流量),记为f(u,v);

         网络流具有以下性质:

                        1.边所容纳的流量<=该边的最大容量.

                        2.反对称性,即f[u][v]=-f[v][u].

                        3.从源点流出的流量总是等于汇点汇聚的流量,对于其他的普通点来说,入度来的流量和等于出度出去的流量和.

    最大网络流:网络流量图G中,最大的可行流称为网络最大流

    我们定义:
    源点:只有流出去的点
    汇点:只有流进来的点
    流量:一条边上流过的流量
    容量:一条边上可供流过的最大流量
    残量:一条边上的容量-流量

    增广路:找到一条从源点到汇点的路径,使得路径上任意一条边的残量>0(注意是大于而不是大于等于,这意味着这条边还可以分配流量),这条路径便称为增广路

    EK:不断找到一条起点到终点的路径,若有,找出这条路径上每一条边的最小值,然后将这条路上的每一条正向边减去这条路的流量,再在这条路上的每一条反向边加上这条边的流量,再在剩下的图上寻找新的路径,使总流量增加。然后形成新的图,再寻找新路径…..直到找不到从起点到终点的路径为止,最大流就算出来了。

    #include<bits/stdc++.h>
    using namespace std;
    int st[1000010],vis[1000010],n,m,s,flow[1000010],maxflow,t,x,y,z,tot=-1,pre[1000010],q[1000010];
    struct node
    {
        int from,to,val,last;
    /*
    from记录起点
    to记录重点
    val记录这条边的流量
    last记录起点和当前这条边的起点一样的边的序号 
    */
    }e[1000001];
    int mn(int x,int y)
    {
        if(x>y)
            return y;
        return x;
    }
    void add(int f,int t,int v)
    {//链式前向星 
        tot++;//当前这条边的序号 
        e[tot].from=f; 
        e[tot].to=t; 
        e[tot].val=v; 
        e[tot].last=st[f]; 
        st[f]=tot; 
        return ; 
    } 
    bool bfs(int s,int m)//寻找从起点到终点的路径 
    {
        int t=0,h=1;
        for(int i=1;i<=n;i++)
        {
            vis[i]=-1;//一开始把所有的点(包括起点和终点)标志为没有走过 
            pre[i]=-1;//记录上一条到达i点的边的起点的编号 
        }
        t++;
        q[t]=s;//将起点入队
        vis[s]=1; 
        flow[s]=2147483647;
        while(t>=h)
        {
            int u=q[h];//拿出队首 
            vis[u]=0;//当前的队首标志为不在队列里 
            h++;//踢出队首 
            for(int i=st[u];i!=0;i=e[i].last)
            {
                int v=e[i].to;
                if(e[i].val!=0 && vis[v]==-1)//如果当前遍历到的点不在队列里(避免重复入队)就入队 
                {
                    flow[v]=mn(flow[u],e[i].val);//更新这条路径的流量(比较,求出最小值) 
                    pre[v]=i;//编号为i的这条边到达了v点,更新pre[v] 
                    t++;//入队 
                    q[t]=v; //
                    vis[v]=u; 
                } 
            } 
        } 
        if(vis[m]!=-1)//从当前点s可以走到点m 
            return true; 
        return false;//如果运行到了这里,说明再也找不到从起点到终点的路了 
    } 
    void update(int s,int t)//一个回溯过程 
    {//如果能进这个函数,说明从当前点s可以走到点t 
        int now=t;//从终点往回查找 
        while(now!=s)//如果当前回溯到的点不是起点 
        {//继续找
            int i=pre[now];
            e[i].val-=flow[t];//正向边加上这条边的流量 
            e[i^1].val+=flow[t];//建反向边,减去这条边的流量 
            now=e[i].from;//更新当前回溯到的点 
        }
        maxflow+=flow[t];//加上当前这条边的流量 
    }
    void EK(int s,int t)
    {
        maxflow=0;//重置最大流 
        while(bfs(s,t)==true)//如果当前找得到一条从起点到终点的路 
            update(s,t);//加上这条路的流量 
    }
    int main()
    {
        scanf("%d %d %d %d",&n,&m,&s,&t);
        for(int i=1;i<=m;i++)
        {
            scanf("%d %d %d",&x,&y,&z);
            add(x,y,z);//建正向边 
            add(y,x,0);//建反向边 
        }
        EK(s,t);//求最大流 
        printf("%d",maxflow);//输出 
        return 0;
    }
  • 相关阅读:
    转义将正文中换行符
    THINKPHP短链接设置方法(路由设置)
    MEMCACHE分布式算法(PHP)
    win7下memCache安装过程
    JQUERY根据值将input控件选中!
    Thinkphp CURD中的where方法
    SQL语法LPAD和RPAD
    Linux查看docker容器日志
    Linux中清空docker容器日志
    错误记录——fail: Microsoft.AspNetCore.Server.Kestrel[13]
  • 原文地址:https://www.cnblogs.com/iloveysm/p/12297013.html
Copyright © 2020-2023  润新知