Dinic 快速网络流算法(模板)

5 篇文章 0 订阅
1 篇文章 0 订阅

一般的网络流算法,每进行一次增广,都要做 一遍BFS,十分浪费。能否少做几次BFS? 这就是Dinic算法要解决的问题。

Edmonds-Karp的提高余地:需要多次从s到t调 用BFS,可以设法减少调用次数。亦即:使用一种代价较小的高效增广方法。  考虑:在一次增广的过程中,寻找多条增广路 径。然后进行 DFS。

先利用 BFS对残余网络分层,每一个节点的层数就是从源点到它最少要经历的边数。利用BFS对残余网络进行分层,然后再用DFS向前一层向后一层不断查找增广路(即要求DFS的每一步都必须走到下一层的节点处)。因此,前面在分层时,只要进行到汇点的层次数被算出 即可停止,因为按照该DFS的规则,和汇点同层或更下一层的节点,是不可能走到汇点的。DFS过程中,要是碰到了汇点,则说明找到了一条增广路 径。此时要增加总流量的值,消减路径上各边的容量 ,并添加反向边,即所谓的进行增广。

DFS找到一条增广路径后,并不立即结束,而是回溯后继 续DFS寻找下一个增广路径。 回溯到哪个节点呢? 回溯到的节点u满足以下条件: 1) DFS搜索树的树边(u,v)上的容量已经变成0。即刚刚 找到的增广路径上所增加的流量,等于(u,v)本次增广 前的容量。(DFS的过程中,是从u走到更下层的v的) 2)u是满足条件 1)的最上层的节点 如果回溯到源点而且无法继续往下走了,DFS结束。 因此,一次DFS过程中,可以找到多条增广路径。 DFS结束后,对残余网络再次进行分层,然后再进行DFS 当残余网络的分层操作无法算出汇点的层次(即BFS到达 不了汇点)时,算法结束,最大流求出。 一般用栈实现DFS,这样就能从栈中提取出增广路径。

将原图备份,原图上的边的容量减去做完最大 流的残余网络上的边的剩余容量,就是边的 流量。

#include<bits/stdc++.h>
#define ll long long
#define INF 999999999
using namespace std;
const ll mod=1e9+7;
const ll maxn=1e6+7;
int g[300][300];
bool vis[300];
int layer[300];
int n,m;
bool countlayer()
{
    int la=0;
    deque<int>q;
    memset(layer,0xff,sizeof(layer));
    q.push_back(1);
    layer[1]=0;
    while(!q.empty())
    {
        int v=q.front();
        q.pop_front();
        for(int j=1; j<=m; j++)
        {
            if(g[v][j]>0&&layer[j]==-1)
            {
                layer[j]=layer[v]+1;
                if(j==m)
                {
                    return true;
                }
                else
                {
                    q.push_back(j);
                }
            }
        }
    }
    return false;
}
int dinic()
{
    int i;
    int s;
    int nm=0;
    deque<int>q;
    while(countlayer())
    {
        q.push_back(1);
        memset(vis,0,sizeof(vis));
        vis[1]=1;
        while(!q.empty())
        {
            int nd=q.back();
            if(nd==m)
            {
                int nmc=INF;
                int nmc_vs;
                for(i=1; i<q.size(); i++)
                {
                    int vs=q[i-1];
                    int ve=q[i];
                    if(g[vs][ve]>0)
                    {
                        if(nmc>g[vs][ve])
                        {
                            nmc=g[vs][ve];
                            nmc_vs=vs;
                        }

                    }
                }
                nm+=nmc;
                for(i=1; i<q.size(); i++)
                {
                    int vs=q[i-1];
                    int ve=q[i];
                    g[vs][ve]-=nmc;
                    g[ve][vs]+=nmc;
                }
                while(!q.empty()&&q.back()!=nmc_vs)
                {
                    vis[q.back()]=0;
                    q.pop_back();
                }
            }else
            {
                for(i=1;i<=m;i++)
                {
                    if(g[nd][i]>0&&layer[i]==layer[nd]+1&&!vis[i])
                    {
                        vis[i]=1;
                        q.push_back(i);
                        break;
                    }
                }
                if(i>m)
                {
                    q.pop_back();
                }
            }

        }
    }
    return nm;
}
int main()
{
    ll i,j,k;
    while(scanf("%lld %lld",&n,&m)!=EOF)
    {
        ll a,b,c;
        memset(g,0,sizeof(g));
        for(i=0;i<n;i++)
        {
            scanf("%lld %lld %lld",&a,&b,&c);
            g[a][b]+=c;
        }
        printf("%lld\n",dinic());

    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值