网络流最大流学习心得

本文介绍了最大流问题中的Ford-Fulkerson和Dinic算法,探讨了它们的工作原理和效率。Ford-Fulkerson算法通过寻找增广路径更新流,而Dinic算法利用分层图优化,速度更快。Dinic算法还引入了当前弧优化,避免了无效边的多次检查,适合处理大规模问题。对于初学者,建议直接学习Dinic算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Ford-Fulkerson

起点是s,终点是t

残余网络:满足f(e)<c(e)的边和f(e)>0的反向边(这个边是一开始就可以加上去,设为0)这是一个寻找增广路的工具,

这里涉及到一个退流的概念,如果找到一条可以更新的路径,那么可能需要把原来的某段流退回去一部分(当然也有可能完全不退)

增广路:利用残余网络找到的s-t路径

我们可以用最大流最小割定理间接证明这个算法是正确的。

算法实现:不断对残余网络进行搜索,知道找不到增广路为止

算法复杂度为o(FV),实际应该比估算要快一点

当顶点数较多或者流的总量较大是这个算法偏慢。

int dfs(int v,int t,int f)
{
    if(v==t) return f;
    use[v]=1;
    for(int i=0;i<tu[v].size();i++)
    {
        node &e=tu[v][i];
        if(!use[e.to]&&e.cap>0)
        {
            int d=dfs(e.to,t,min(f,e.cap));
            if(d>0)
            {
                e.cap-=d;
                tu[e.to][e.wei].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s,int t)
{
    int flow=0;
    for(;;)
    {
        memset(use,0,sizeof(use));
        int f=dfs(s,t,1e8);
        if(f==0) return flow;
        flow+=f;
    }
}

Dinic

这个算法比上述的要快。

这里引入一个分层图的概念,不断得寻找最短增广路,可以得出每跑完一个分层图之后最短增广路都会至少加一。

why?在当前分层图无法在找到增广路的情况下,要么就是不存在增广路了,要么就是最短增广路变长了(不可能变短了)

多次考虑当前从起点到终点的最短路径,就多次bfs,对于每次的bfs找最短路,对于任何一条最短路我们寻找增广路,由于最短路的长度不会超过n,

如果再加入一个当前弧优化(也就是避免对无用边的多次检查),最后的复杂度不会超过上界O(nm^2) 这样对于一些最大流上界较大的题就比fulkerson快了很多。

提到当前弧优化其实不得不提一下EK算法,就是每次bfs寻找一个增量,路径中流量严格小于容量的增量,作为一条增光路,由于是bfs所以需要记录一下路径的轨迹。

Dinic相对于EK的优化在于,同一增光路长度的增光路一次性全找出来,然后可以忽略掉无用边的遍历。

总的来说,Ford-Fulkerson就是废物,连EK都比它快,初学者其实没必要学前者,至于Dinic就更不用说了。三者的核心都是增光路定理。

而且,实际上应用中速度会非常快,由于上界比较松的原因。

这里给一发dinic的板子:

void jian(int s,int t,long long val)//这里是jian图
{
    // cout<<"val="<<val<<endl;
    a[s].push_back(node{t,val,a[t].size()});
    a[t].push_back(node{s,0,a[s].size()-1});
}
long long dfs(int s,int t,long long f)
{
    // cout<<"f="<<f<<endl;
    // cout<<s<<" "<<t<<endl;
    if(s==t)
    {
        // cout<<"f="<<f<<endl;
        return f;
    }
    // use[s]=1;
    int tt=a[s].size();
    // cout<<tt<<endl;
    for(int &i=iter[s];i<tt;i++)//当前弧优化
    {
        node &e=a[s][i];
        // cout<<e.to<<" "<<e.val<<endl;
        if(level[e.to]>level[s] && e.val>0)
        {
            // cout<<"fsjldf"<<endl;
            long long  d=dfs(e.to,t,min(f,e.val));
            // cout<<"d="<<d<<endl;
            if(d>0) 
            {
                e.val-=d;
                a[e.to][e.fan].val+=d;
                return d;
            }
        }
    }
    return 0;
}
void bfs(int s)
{
    memset(level,-1,sizeof(level));
    queue<int> que;
    level[s]=0;
    que.push(s);
    while(!que.empty())
    {
        int v=que.front();que.pop();
        for(int i=0;i<a[v].size();i++)
        {
            node &e=a[v][i];
            if(e.val>0 && level[e.to]<0)
            {
                level[e.to]=level[v]+1;
                que.push(e.to);
            }
        }
    }
}

long long daliu(int s,int t)//显然是最大流的意思
{
    long long flow=0;
    while(1)
    {
    bfs(s);
    // cout<<level[t]<<endl;
    if(level[t]<0) return flow;
    memset(iter,0,sizeof(iter));
    long long f;
    // cout<<"flow="<<flow<<endl;
    while((f=dfs(s,t,inf))>0)
    {
    // cout<<"f="<<f<<endl;
      flow+=f; 
    //   cout<<"flow="<<flow<<endl;
    }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值