• 网络流算法详解


    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;
    }
  • 相关阅读:
    如何禁止在DBGRID末位自动添加一行记录
    DELPHI加密字串(异或运算加密)
    SQL SERVER 正则替换
    sql里的正则表达式
    UFIDA
    delphi raise 语句: 抛出异常
    delphi怎么一次性动态删除(释放)数个动态创建的组件?
    Delphi动态创建组件,并释放内存
    DELPHI 动态 创建和释放 多个 EDIT 控件
    禁止在DBGrid中按delete删除记录
  • 原文地址:https://www.cnblogs.com/kamimxr/p/11200665.html
Copyright © 2020-2023  润新知