网络最大流

关于 vector 存图

很多网上的资料(视频、题解)的最大流算法为了方便找反边,都使用了链式前向星。

但是!

vector 党表示不服!

于是在进行学习后,笔者归纳出了两种 vector 存图并快速找反边的方法。

存储反边编号

一般 vector 实现邻接表是形如这样的:(在最大流相关算法中)

struct edge
{
    int v,w;
};
vector<edge> e[N];
inline void addedge(int u,int v,int w)
{
    e[u].push_back({v,w});
    e[v].push_back({u,0});
}

但是这种存储方法无法快速找到反边。
考虑在结构体 edge 中添加一个信息 x

struct edge
{
    int v,w,x;
};

表示反边为 e[v][x]。那么加边时也相应的需要修改:

inline void addedge(int u,int v,int w)
{
    e[u].push_back({v,w,int(e[v].size())});
    e[v].push_back({u,0,int(e[u].size()-1)});
}

这样就可以快速实现找反边操作了。(关于为什么是 int(e[v].size())int(e[u].size()-1) 请自行思考。)

注意,EK 算法中 int pre[N] 数组则需要改成 pair<int,int> pre[N],分别表示来自 first 号点和它的 second 号边。

这里放出 EK 板子和 Dinic 板子:

EK:

#include <iostream>
#include <vector>
#include <bitset>
#include <queue>
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
constexpr int N=214;
constexpr ll INF=0x3f3f3f3f3f3f3f3f;
struct edge
{
    int v;
    ll c;
    int x;
};

vector<edge> e[N];

int n,m,s,t;

namespace EK
{
    bitset<N> vis;
    pii pre[N];
    ll flow[N];
    bool bfs()
    {
        queue<int> q;
        vis.reset();
        vis[s]=true;
        q.push(s);
        flow[s]=INF;
        int u,l,v;
        ll c;
        while(!q.empty())
        {
            u=q.front();
            q.pop();
            l=e[u].size();
            for(int i=0;i<l;i++)
            {
                v=e[u][i].v;
                c=e[u][i].c;
                if(!vis[v]&&c>0)
                {
                    vis[v]=true;
                    flow[v]=min(flow[u],c);
                    pre[v]={u,i};
                    if(v==t)return true;
                    q.push(v);
                }
            }
        }
        return false;
    }
    ll EK()
    {
        ll r=0ll;
        while(bfs())
        {
            r+=flow[t];
            for(int i=t;i!=s;i=pre[i].first)
            {
                e[pre[i].first][pre[i].second].c-=flow[t];
                e[i][e[pre[i].first][pre[i].second].x].c+=flow[t];
            }
        }
        return r;
    }
}//^ namespace EK

int main()
{
    scanf("%d %d %d %d",&n,&m,&s,&t);
    for(int i=1,u,v,w;i<=m;i++)
    {
        scanf("%d %d %d",&u,&v,&w);
        e[u].push_back({v,ll(w),int(e[v].size())});
        e[v].push_back({u,0ll,int(e[u].size()-1)});
    }
    printf("%lld",EK::EK());
    return 0;
}

Dinic:

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
constexpr int N=214;
constexpr ll INF=0x3f3f3f3f3f3f3f3f;
struct edge
{
    int v;
    ll c;
    int x;
};

vector<edge> e[N];

int n,m,s,t;

namespace Dinic
{
    int dis[N],fir[N];
    bool bfs()
    {
        fill(fir,fir+N,0);
        fill(dis,dis+N,0);
        dis[s]=1;
        queue<int> q;
        q.push(s);
        int u,l,v;
        while(!q.empty())
        {
            u=q.front();
            q.pop();
            l=e[u].size();
            for(int i=0;i<l;i++)
            {
                v=e[u][i].v;
                if(!dis[v]&&e[u][i].c>0ll)
                {
                    dis[v]=dis[u]+1;
                    q.push(v);
                    if(v==t)return true;
                }
            }
        }
        return false;
    }
    ll dfs(int u=s,ll flow=INF)
    {
        if(u==t)return flow;
        int l=e[u].size(),v;
        ll res=0ll,now,c;
        for(int i=fir[u];i<l;i++)
        {
            fir[u]=i;
            v=e[u][i].v;
            c=e[u][i].c;
            if(dis[v]==dis[u]+1&&c>0ll)
            {
                now=dfs(v,min(flow,c));
                if(now==0ll)dis[v]=0;
                e[u][i].c-=now;
                e[v][e[u][i].x].c+=now;
                res+=now;
                flow-=now;
                if(flow==0ll)return res;
            }
        }
        return res;
    }
    ll Dinic()
    {
        ll r=0ll;
        while(bfs())r+=dfs();
        return r;
    }
}//^ namespace Dinic

int main()
{
    scanf("%d %d %d %d",&n,&m,&s,&t);
    for(int i=1,u,v,w;i<=m;i++)
    {
        scanf("%d %d %d",&u,&v,&w);
        e[u].push_back({v,ll(w),int(e[v].size())});
        e[v].push_back({u,0ll,int(e[u].size()-1)});
    }
    printf("%lld",Dinic::Dinic());
    return 0;
}

将边存入池子并保存编号

我们可以使用两个 vector 实现更方便的存边方式:

vector<edge> es;
vector<int> e[N];

其中 es 存了所有边e[u] 中存 u 的所有出边es 中的编号

于是,如果我们需要知道边 e[u][i] 的信息,只需要访问 es[e[u][i]]

e[u][i] 的反边即为 e[u][i]^1,与传统链式前向星的访问反边方式类似。

存边时:

e[u].push_back(es.size());
es.push_back({v,ll(w)});
e[v].push_back(es.size());
es.push_back({u,0ll});

依然给出板子:

EK:

#include <iostream>
#include <vector>
#include <bitset>
#include <queue>
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
constexpr int N=214;
constexpr ll INF=0x3f3f3f3f3f3f3f3f;
struct edge
{
    int v;
    ll c;
};
vector<edge> es;
vector<int> e[N];

int n,m,s,t;

namespace EK
{
    bitset<N> vis;
    int pre[N];
    ll flow[N];
    bool bfs()
    {
        queue<int> q;
        vis.reset();
        vis[s]=true;
        q.push(s);
        flow[s]=INF;
        int u,l,v;
        ll c;
        while(!q.empty())
        {
            u=q.front();
            q.pop();
            l=e[u].size();
            for(int i=0;i<l;i++)
            {
                v=es[e[u][i]].v;
                c=es[e[u][i]].c;
                if(!vis[v]&&c>0)
                {
                    vis[v]=true;
                    flow[v]=min(flow[u],c);
                    pre[v]=e[u][i];
                    if(v==t)return true;
                    q.push(v);
                }
            }
        }
        return false;
    }
    ll EK()
    {
        ll r=0ll;
        while(bfs())
        {
            r+=flow[t];
            for(int i=t;i!=s;i=es[pre[i]^1].v)
            {
                es[pre[i]].c-=flow[t];
                es[pre[i]^1].c+=flow[t];
            }
        }
        return r;
    }
}//^ namespace EK

int main()
{
    scanf("%d %d %d %d",&n,&m,&s,&t);
    for(int i=1,u,v,w;i<=m;i++)
    {
        scanf("%d %d %d",&u,&v,&w);
        e[u].push_back(es.size());
        es.push_back({v,ll(w)});
        e[v].push_back(es.size());
        es.push_back({u,0ll});
    }
    printf("%lld",EK::EK());
    return 0;
}

Dinic:

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
constexpr int N=214;
constexpr ll INF=0x3f3f3f3f3f3f3f3f;
struct edge
{
    int v;
    ll c;
};

vector<edge> es;
vector<int> e[N];

int n,m,s,t;

namespace Dinic
{
    int dis[N],fir[N];
    bool bfs()
    {
        fill(fir,fir+N,0);
        fill(dis,dis+N,0);
        dis[s]=1;
        queue<int> q;
        q.push(s);
        int u,l,v;
        while(!q.empty())
        {
            u=q.front();
            q.pop();
            l=e[u].size();
            for(int i=0;i<l;i++)
            {
                v=es[e[u][i]].v;
                if(!dis[v]&&es[e[u][i]].c>0ll)
                {
                    dis[v]=dis[u]+1;
                    q.push(v);
                    if(v==t)return true;
                }
            }
        }
        return false;
    }
    ll dfs(int u=s,ll flow=INF)
    {
        if(u==t)return flow;
        int l=e[u].size(),v;
        ll res=0ll,now,c;
        for(int i=fir[u];i<l;i++)
        {
            fir[u]=i;
            v=es[e[u][i]].v;
            c=es[e[u][i]].c;
            if(dis[v]==dis[u]+1&&c>0ll)
            {
                now=dfs(v,min(flow,c));
                if(now==0ll)dis[v]=0;
                es[e[u][i]].c-=now;
                es[e[u][i]^1].c+=now;
                res+=now;
                flow-=now;
                if(flow==0ll)return res;
            }
        }
        return res;
    }
    ll Dinic()
    {
        ll r=0ll;
        while(bfs())r+=dfs();
        return r;
    }
}//^ namespace Dinic

int main()
{
    scanf("%d %d %d %d",&n,&m,&s,&t);
    for(int i=1,u,v,w;i<=m;i++)
    {
        scanf("%d %d %d",&u,&v,&w);
        e[u].push_back(es.size());
        es.push_back({v,ll(w)});
        e[v].push_back(es.size());
        es.push_back({u,0ll});
    }
    printf("%lld",Dinic::Dinic());
    return 0;
}

正文

咕……

网络最大流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值