zoukankan      html  css  js  c++  java
  • # [洛谷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

  • 相关阅读:
    HDU4529 郑厂长系列故事——N骑士问题 —— 状压DP
    POJ1185 炮兵阵地 —— 状压DP
    BZOJ1415 聪聪和可可 —— 期望 记忆化搜索
    TopCoder SRM420 Div1 RedIsGood —— 期望
    LightOJ
    LightOJ
    后缀数组小结
    URAL
    POJ3581 Sequence —— 后缀数组
    hdu 5269 ZYB loves Xor I
  • 原文地址:https://www.cnblogs.com/sstealer/p/12207600.html
Copyright © 2011-2022 走看看