• # [洛谷3376] 网络最大流


    [洛谷3376] 网络最大流

    洛谷3376

    题意

    网络最大流模板题,题意很直白。

    给出源点,汇点,点个数,边条数,以及每条边的流量,求源点到汇点的最大流量。

    思路

    Dinic算法简介

    最大流经典算法Dinic算法,理论复杂度(O(n^2m)),但是实际运用中远远达不到这个上界,可以说是比较容易实现的效率最高的网络流算法之一,一般能处理(10^4 -10^5)规模的网络,特别的地,Dinic算法求解二分图最大匹配的理论时间复杂度为(O(msqrt{n})),实际表现则更快。

    实现步骤

    1. 在残量网络(剩余流量大于0的子图)上BFS求出节点的层次,构造分层图
    2. 在分层图上DFS寻找增广路,在回溯时实时更新剩余流量,每个点可以流向多条边。

    需要注意的点:Dinic算法继承了Ek算法反向边的思想,同意^1操作实现正反向边的互相转化,需要注意的是,正向边必须从偶数开始,对应的方向变就是正向边序号加一,正向边一般从0开始编号。

    优化技巧

    1. 当前弧优化

      一个点可能有多条出边,在DFS寻找增广路的时候,如果遍历该点的出边时,发现有些边已经没有剩余流量时,记录下当前遍历的点,下次再碰到该点时,无需从头开始遍历,直接从记录的位置开始遍历(因为记录位置之前的出边都没有剩余流量了,没有遍历的必要,代码实现:边使用链式前向星存储,使用额外数组cur[]复制head[]的值,实现cur代替head进行遍历,并及时更新cur的值,也就是修改头结点的指向)。

    2. 多路增广优化
      一个点有多条出边,一次性处理多条边的流量,而不是对每条边流量的处理都重新调用一个函数

    3. 爆点优化

      当DFS遍历时发现一个点没有了流出的流量,则把该点的deep值置为-2,将无用的点抛弃,下次无需再遍历。

    代码实现

    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    #define sf(x) scanf("%d",&(x))
    
    const int MAXN=1e4+5;
    const int MAXM=1e5+5;
    const int INF=1e8+10;
    
    int N,M,S,T;
    struct node{int v,flow,next;}edge[MAXM*2];
    
    int head[MAXN],cur[MAXN],num=0;//注意这里必须从0开始
    
    inline void add(int x,int y,int z)
    {
        edge[num].v=y;edge[num].flow=z;
        edge[num].next=head[x];head[x]=num++;
    }
    queue<int> q;
    int deep[MAXN];
    
    bool BFS()
    {
        memset(deep,0,sizeof(deep));
        deep[S]=1;
       q.push(S);
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=head[u];i!=-1;i=edge[i].next)
                if(!deep[edge[i].v]&&edge[i].flow)
                {
                    deep[edge[i].v]=deep[u]+1;
                    q.push(edge[i].v);
                }
        }
        return deep[T];
    }
    
    int DFS(int now,int nowflow)
    {
        if(now==T)    return nowflow;
        int totflow=0;//从这个点总共可以增广多少流量
        for(int i=cur[now];i!=-1;i=edge[i].next)
        {
            cur[now]=i;//当前弧优化,记录压榨到哪条边
    
            if(deep[edge[i].v]==deep[now]+1&&edge[i].flow)//只有满足距离要求与流量要求的点才能进行增广
            {
                int canflow=DFS(edge[i].v,min(nowflow,edge[i].flow));
    
                if(!canflow)continue;//当可增广的流量大于0,更新
                edge[i].flow-=canflow;edge[i^1].flow+=canflow;//增广
                totflow+=canflow;
                nowflow-=canflow;
                if(nowflow<=0) break; //当前点已经没有流量  快100ms
            }
        }
        if(!totflow)deep[now]=-2;//爆点优化,该点已经没有剩余流量,证明不必要的点下一次不需要遍历
        return totflow;//返回总流量,多路增广优化
    }
    void Dinic()
    {
        int ans=0;
        while(BFS())//每次构造分层图
        {
            memcpy(cur,head,sizeof(head)); //当前弧优化
            ans+=DFS(S,INF);//进行增广
        }
        printf("%d",ans);
    }
    int main()
    {
        sf(N);sf(M);sf(S);sf(T);
        memset(head,-1,sizeof(head));
        for(int i=1;i<=M;i++)
        {
            int x,y,z;
            sf(x);sf(y);sf(z);
            add(x,y,z),add(y,x,0);
        }
        Dinic();
        return  0;
    }
    

    参考博文:
    https://www.cnblogs.com/zwfymqz/p/8280746.html#_label4
    https://www.bilibili.com/video/av21945401?from=search&seid=17416394282430843291
    https://www.cnblogs.com/SYCstudio/p/7260613.html

  • 相关阅读:
    20191119PHP.class类练习
    20191115PHP cookie登入实例
    Jenkins详细教程
    Navicat 破解
    测试_离职_交接内容
    ETL方法与过程讲
    ETL测试或数据仓库测试入门
    大数据基础了解-(基础01)
    adb调试显示adb: usage: unknown command device
    hive 创建/删除/截断 表(翻译自Hive wiki)
  • 原文地址:https://www.cnblogs.com/sstealer/p/12207600.html
Copyright © 2020-2023  润新知