# [洛谷3376] 网络最大流模板题

不羁的心 提交于 2020-01-17 22:25:58

[洛谷3376] 网络最大流模板题

洛谷3376

题意

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

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

思路

Dinic算法简介

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

实现步骤

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

优化技巧

  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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!