网络流算法详解

what is 网络流

相信大家都听过网络流这个名字。哦?你知道网络,还知道输入输出流?呀,看来你已经会网络流了啊!

嗯,开个玩笑

网络流(network-flows)是一种图论算法,说起来可能比较抽象,不过我们可以把网络图想象成一个水管分布图,网络流就相当于水流。边就是水管,节点就是一个转换水流的地方。 

就像上面的图,它其实就是网(shui)络(guan)图,当然,流入你家的可能不止一根水管,那么,如果现在让你分配水怎么流,就是你可以控制水到了某一个点后往哪根水管流,怎么使流入你家的水最多,就是最大流问题。

另外最小割是什么呢?

就是如果有恐怖分子要拆水管,那么他割宽度为5的水管就要相应耗费5单位的力,所以他想耗尽量少的力去让你家没有水,那么他最终耗费的最小的力就是最小割。


网络流的基本性质

1、流量平衡:即 出流的量=入流的量 ,这个比较好理解,就是说如果你入流了100单位水,可水管只能流出50单位水,那么那另外50单位水就爆水管了。

2、最小割=最大流:如果恐怖分子知道你怎么分配使你家的流量最大,那么他就可以把所有水流的路径中最小的割了,那其实就是最大流其中一条路径所能提供的水量。

最大流算法

dinic

在学dinic之前,希望大家先明白EK的实现思路和原理。讲dinic会在EK的基础上讲的。

其实dinic和EK差不多,只不过dinic多了个分层,这个所谓的分层是一个限制,EK在bfs不是只要有路走就增广吗?而dinic则限制只能往下一层走。

那么来讲讲dinic怎么实现吧:

首先是分层,dinic分层用bfs来分层,这个没什么好说的,就是源点是第0层,和源点有边的点是第1层,这样bfs下去分层。当然,在分层中还可以判断汇点还有没有流可以汇入,如果汇点分不了层了,就代表没有流可以流入汇点了。

然后是找最大流,dinic的找最大流是用dfs的。具体就是从源点找到满足的点就一直递归下去,直到找到汇点,返回中途的最小值,然后更新边权,和EK一样,不过dinic是递归形式,所以可以直接在dfs上更新,不需要在写个update操作。

网络流3

下面看看dinic的实现:

#include <bits/stdc++.h>
using namespace std;
struct littlestar{
    int to;
    int nxt;
    int w;
}star[200020];
int head[200020],cnt=1;
void add(int u,int v,int w)
{
    star[++cnt].to=v;
    star[cnt].w=w;
    star[cnt].nxt=head[u];
    head[u]=cnt;
}
int n,m,s,t;
queue<int> q;
int dep[20020];
bool bfs()
{
    memset(dep,0,sizeof(dep));
    while(q.size()) q.pop();
    q.push(s);
    dep[s]=1;
    while(q.size()){
        int tmp=q.front();
        q.pop();
        for(int i=head[tmp];i;i=star[i].nxt){
            int v=star[i].to;
            if(!dep[v] && star[i].w){
                dep[v]=dep[tmp]+1;
                q.push(v);
                if(v==t) return 1;
            }
        }
    }
    return 0;
}
int maxn,inf=1<<29;
int dinic(int u,int flow)
{
    if(u==t) return flow;
    int rest=flow;
    int k;
    for(int i=head[u];i && rest;i=star[i].nxt){
        int v=star[i].to;
        if(star[i].w && dep[v]==dep[u]+1){
            k=dinic(v,min(star[i].w,rest));
            if(!k) dep[v]=0; //剪枝 
            star[i].w-=k;
            star[i^1].w+=k;
            rest-=k;
        }
    }
    return flow-rest;
}
int main ()
{
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,0);
    }
    int flow=0;
    while(bfs()){
        while((flow=dinic(s,inf))) maxn+=flow;
    }
    cout<<maxn;
}

 

最小费用最大流:

就是把dinic中的bfs换成spfa就可以了

 

#include <bits/stdc++.h>
using namespace std;
struct littlestar{
    int to;
    int nxt;
    int flow;
    int w;
}star[200020];
int head[200020],cnt=1;
void add(int u,int v,int flow,int w)
{
    star[++cnt].to=v;
    star[cnt].w=w;
    star[cnt].flow=flow;
    star[cnt].nxt=head[u];
    head[u]=cnt;
}
int n,m,s,t;
int dis[200020],vis[200020],flow[200020],pre[200020];
int pre2[200020];
queue<int> q;
int SPFA(int s,int t)
{
    memset(dis,0x7f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    memset(flow,0x7f,sizeof(flow));
    q.push(s);
    dis[s]=0;vis[s]=1;pre[t]=-1;
    while(q.size()){
        int now=q.front();
        q.pop();
        vis[now]=0;
        for(int i=head[now];i;i=star[i].nxt){
            int v=star[i].to;
            if(star[i].flow>0&&dis[v]>dis[now]+star[i].w){
                dis[v]=dis[now]+star[i].w;
                pre2[v]=i;
                pre[v]=now;
                flow[v]=min(flow[now],star[i].flow);
                if(!vis[v]){
                    vis[v]=1;
                    q.push(v);        
                }
            }
        }
    }
    return pre[t]!=-1;
}
int mc,mf;
int main ()
{
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(int i=1;i<=m;i++){
        int a,b,c,d;
        cin>>a>>b>>c>>d;
        add(a,b,c,d);
        add(b,a,0,-d);
    }
    while(SPFA(s,t)){
        int now=t;
        mf+=flow[t];
        mc+=flow[t]*dis[t];
        while(now!=s){
            star[pre2[now]].flow-=flow[t];
            star[pre2[now]^1].flow+=flow[t];
            now=pre[now];
        }
    }
    cout<<mf<<" "<<mc;
}

 

posted @ 2019-07-17 14:06  神之右大臣  阅读(5358)  评论(1编辑  收藏  举报