【网络流自学】最大流模板 EK 与 Dinic

过两天要讲优化最大流算法,今天抓紧先看看两个最基本的最大流算法。
网络流可以形象地理解为“水流”,所谓最大流问题,就是水要从源点出发流经一个有向图到达汇点,问能流到汇点的最大水流量,当然有向图的每条边有边权(容量),如果水要从某条路到达汇点,显然水流量不能超过这条路上的最小边权(木桶效应)。
图上能够使总流量变大的路称为增广路。
通过求取最短增广路不断更新最大流的算法就是EK算法,而对图分层并用dfs回溯寻找增广路的算法是Dinic算法。
这是题目
这是代码:
Dinic算法,时间复杂度O(NNM)

//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cstdlib>
#include <queue>
#include <map>
#define ll long long
using namespace std;
const int maxn=1e4+5;
const int maxm=1e5+5;
const int inf = 2e9;
int p=1,h[maxn<<1],nxt[maxm<<1],w[maxm<<1],v[maxm<<1],depth[maxn],nh[maxn<<1];
int n,m;
bool vis[maxm<<1];
void add(int a,int b,int c)
{
    p++;
    v[p]=b;
    w[p]=c;
    nxt[p]=h[a];
    h[a]=p;

}
bool bfs(int s,int t)
{
    memset(depth,0x7f, sizeof(depth));
    queue<int>q;
    depth[s]=0;
    for(int i=1;i<=n;i++)nh[i]=h[i];
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=h[u];i;i=nxt[i])
        {
            int to=v[i];
            if(depth[to]>inf && w[i])
            {
                depth[to]=depth[u]+1;
                q.push(to);
            }
        }
    }
    if(depth[t] < inf)return 1;
    return 0;
}
int dfs(int x,int t,int limit)
{
    if(!limit || x==t)return limit;
    int flow=0,f=0;
    for(int i=nh[x];i;i=nxt[i])
    {
        nh[x]=i;//一个小优化,记录对于这个点已经处理到了哪条边,因为过程中对于每条边都充分发挥了其价值,这样可以减少遍历次数
        int to=v[i];
        if(depth[to]==depth[x]+1 && (f=dfs(to,t,min(limit,w[i]))))
        {
            flow+=f;
            limit-=f;
            w[i]-=f;
            w[i^1]+=f;
            if(!limit)break;
        }
    }
    return flow;
}
int dinic(int s,int t)
{
    int maxflow=0;
    while(bfs(s,t))
    {
        maxflow+=dfs(s,t,inf);
    }
    return maxflow;
}
int getnum()
{
    int num=0;
    bool f=1;
    char c=getchar();
    while (!isdigit(c))
    {
        if(c=='-')f=0;
        c=getchar();
    }
    while(isdigit(c))
    {
        num=num*10+c-'0';
        c=getchar();
    }
    return f ? num : -num;
}
int main()
{
    n=getnum(),m=getnum();
    int st=getnum(),en=getnum();
    for(int i=1;i<=m;i++)
    {
        int a=getnum(),b=getnum(),c=getnum();
        add(a,b,c);
        add(b,a,0);
    }
    printf("%d",dinic(st,en));
    return 0;
}

EK算法,时间复杂度O(NMM)


#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cstdlib>
#include <queue>
#include <map>
#define ll long long
using namespace std;
const int maxn=1e5+5;
const int maxm=1e5+5;
int p=1,h[maxn<<1],nxt[maxm<<1],w[maxm<<1],v[maxm<<1];
bool vv[maxn<<1];
int n,m,s,t;
struct node
{
    int edge;
    int u;
}pre[maxn<<1];
void add(int a,int b,int c)
{
    p++;
    v[p]=b;
    w[p]=c;
    nxt[p]=h[a];
    h[a]=p;
}
bool bfs()
{
    queue<int>q;
    memset(vv,0,sizeof(vv));
    memset(pre,-1,sizeof(pre));
    vv[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=h[u];i>=1;i=nxt[i])
        {
            int to=v[i];
            if(!vv[to]&&w[i]>0)
            {
                pre[to].edge=i;
                pre[to].u=u;
                if(to==t)return 1;
                vv[to]=1;
                q.push(to);
            }
        }
    }
    return 0;
}
int EK()
{
    int ans=0;
    while(bfs())
    {
        int mi=1<<30;
        for(int i=t;i!=s;i=pre[i].u)
        {
            mi=min(mi,w[pre[i].edge]);
        }
        for(int i=t;i!=s;i=pre[i].u)
        {
            w[pre[i].edge]-=mi;
            w[pre[i].edge^1]+=mi;
        }
        ans+=mi;
    }
    return ans;
}
int getnum()
{
    int num=0;
    bool f=1;
    char c=getchar();
    while (!isdigit(c))
    {
        if(c=='-')f=0;
        c=getchar();
    }
    while(isdigit(c))
    {
        num=num*10+c-'0';
        c=getchar();
    }
    return f ? num : -num;
}
int main()
{
    n=getnum(),m=getnum(),s=getnum(),t=getnum();
    for(int i=1;i<=m;i++)
    {
        int a=getnum(),b=getnum(),c=getnum();
        add(a,b,c);
        add(b,a,0);
    }
    printf("%d",EK());
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值