zoukankan      html  css  js  c++  java
  • 网络流学习

    先把板子放在这里,等时间写讲解。

    FF算法,PP算法算是作为引入,故不加任何优化

    1. FF算法

    FF算法的时间复杂度与流量有关,是可以hack的。在luogu上能取得82分的好成绩

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #define int long long
    using namespace std;
    int cnt=1,n,m,s,t,nowflow,maxflow,h[1010],vis[1010];
    struct edge{int to,nxt,val;}e[10010];
    void addedge(int u,int v,int val)
    {
        e[++cnt]=(edge){v,h[u],val};
        h[u]=cnt;
    }
    int dfs(int u,int flow)
    {
        if(u==t)return flow;
        vis[u]=1;
        for(int i=h[u];i;i=e[i].nxt)
        {
            h[u]=i;
            int p=e[i].to;
            if(vis[p]==1||e[i].val==0)continue;
            int nowflow=dfs(p,min(flow,e[i].val));
            if(nowflow>0)
            {
                e[i].val-=nowflow;
                e[i^1].val+=nowflow;
                return nowflow;
            }
        }
        return 0;
    }
    int ff()
    {
        while(1)
        {
            memset(vis,0,sizeof(vis));
            nowflow=dfs(s,0x7fffffffffffffff);
            if(nowflow<=0)break;
            maxflow+=nowflow;
        }
        return maxflow;
    }
    signed main()
    {
        scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
        for(int i=1;i<=m;i++)
        {
            int uu,vv,vval;
            scanf("%lld%lld%lld",&uu,&vv,&vval);
            addedge(uu,vv,vval);addedge(vv,uu,0);
        }
        printf("%lld
    ",ff());
        return 0;
    }
    
    1. EK算法

    FF算法用的是dfs,效率极其低下。
    我们把FF的dfs换成bfs,这样每次寻找的是最短的增广路径,效率高上不少。
    但还是不够快,不建议写。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <queue>
    #define int long long
    using namespace std;
    int n,m,s,t,cnt=1,maxflow,h[1010],inque[1010];
    queue<int> q;
    struct edge{int to,nxt,val;}e[10010];
    struct path{int nxt,ed;}pre[1010];
    void addedge(int u,int v,int val)
    {
        e[++cnt]=(edge){v,h[u],val};
        h[u]=cnt;
    }
    bool bfs()
    {
        while(!q.empty())q.pop();
        memset(inque,0,sizeof(inque));
        memset(pre,-1,sizeof(pre));
        q.push(s);inque[s]=1;
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=h[u];i;i=e[i].nxt)
            {
                int p=e[i].to;
                if(e[i].val&&!inque[p])
                {
                    pre[p]=(path){u,i};
                    if(p==t)return 1;
                    q.push(p);inque[p]=1;
                }
            }
        }
        return 0;
    }
    int ek()
    {
        while(bfs())
        {
            int nowflow=0x7fffffffffffffff;
            for(int i=t;i!=s;i=pre[i].nxt)nowflow=min(nowflow,e[pre[i].ed].val);
            for(int i=t;i!=s;i=pre[i].nxt)
            {
                e[pre[i].ed].val-=nowflow;
                e[pre[i].ed^1].val+=nowflow;
            }
            maxflow+=nowflow;
        }
        return maxflow;
    }
    signed main()
    {
        scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
        for(int i=1;i<=m;i++)
        {
            int uu,vv,vval;
            scanf("%lld%lld%lld",&uu,&vv,&vval);
            addedge(uu,vv,vval);addedge(vv,uu,0);
        }
        printf("%lld
    ",ek());
        return 0;
    }
    
    1. dinic算法

    主流算法。可以理解为对FF算法的一个大优化。
    我们在dfs前先bfs一遍分好层,这样寻找的也是最短的增广路径。
    然后我们还可以利用dfs的特性一次找多条增广路,从而大大提高了效率。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <queue>
    #define int long long
    using namespace std;
    int cnt=1,n,m,s,t,maxflow,h[1010],dep[1010],inque[1010],cur[1010];
    queue<int> q;
    struct edge{int to,nxt,val;}e[10010];
    void addedge(int u,int v,int val)
    {
        e[++cnt]=(edge){v,h[u],val};
        h[u]=cnt;
    }
    bool bfs()
    {
        memset(dep,0x7f,sizeof(dep));
        memset(inque,0,sizeof(inque));
        memcpy(cur,h,sizeof(h));
        while(!q.empty())q.pop();
        q.push(s);dep[s]=0;
        while(!q.empty())
        {
            int u=q.front();q.pop();
            inque[u]=0;
            for(int i=h[u];i;i=e[i].nxt)
            {
                int p=e[i].to;
                if(e[i].val&&dep[p]>dep[u]+1)
                {
                    dep[p]=dep[u]+1;
                    if(!inque[p])
                    {
                        inque[p]=1;
                        q.push(p);
                    }
                }
            }
        }
        if(dep[t]!=0x7f7f7f7f7f7f7f7f)return 1;
        return 0;
    }
    int dfs(int u,int flow)
    {
        if(u==t)return flow;
        int used=0,nowflow=0;
        for(int i=cur[u];i;i=e[i].nxt)
        {
            cur[u]=i;
            int p=e[i].to;
            if(e[i].val&&dep[p]==dep[u]+1)
            {
                nowflow=dfs(p,min(flow-used,e[i].val));
                if(nowflow)
                {
                    used+=nowflow;
                    e[i].val-=nowflow;
                    e[i^1].val+=nowflow;
                    if(used==flow)break;
                }
            }
        }
        if(!used)dep[u]=-1;
        return used;
    }
    int dinic()
    {
        while(bfs())maxflow+=dfs(s,0x7fffffffffffffff);
        return maxflow;
    }
    signed main()
    {
        scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
        for(int i=1;i<=m;i++)
        {
            int uu,vv,vval;
            scanf("%lld%lld%lld",&uu,&vv,&vval);
            addedge(uu,vv,vval);addedge(vv,uu,0);
        }
        printf("%lld
    ",dinic());
        return 0;
    }
    
    1. ISAP算法

    对dinic的优化。
    通过一些奇怪的技巧只跑一遍bfs从而大大提高了效率。
    最坏时间复杂度同dinic。
    ISAP的代码长度和dinic相当甚至更短
    所以也有些OI选手将ISAP作为平常使用的算法。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <queue>
    #define int long long
    using namespace std;
    int cnt=1,n,m,s,t,maxflow,h[1010],dep[1010],gap[1010],cur[1010];
    queue<int> q;
    struct edge{int to,nxt,val;}e[10010];
    void addedge(int u,int v,int val)
    {
        e[++cnt]=(edge){v,h[u],val};
        h[u]=cnt;
    }
    void bfs()
    {
        memset(dep,-1,sizeof(dep));
        queue<int> q;q.push(t);
        dep[t]=0;gap[0]=1;
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=h[u];i;i=e[i].nxt)
            {
                int p=e[i].to;
                if(dep[p]!=-1)continue;
                q.push(p);
                dep[p]=dep[u]+1;
                gap[dep[p]]++;
            }
        }
        return;
    }
    int dfs(int u,int flow)
    {
        if(u==t)return flow;
        int used=0,nowflow=0;
        for(int i=cur[u];i;i=e[i].nxt)
        {
            cur[u]=i;
            int p=e[i].to;
            if(e[i].val&&dep[p]+1==dep[u])
            {
                nowflow=dfs(p,min(flow-used,e[i].val));
                if(nowflow)
                {
                    used+=nowflow;
                    e[i].val-=nowflow;
                    e[i^1].val+=nowflow;
                }
                if(used==flow)return used;
            }
        }
        gap[dep[u]]--;
        if(gap[dep[u]]==0)dep[s]=n+1;
        dep[u]++;gap[dep[u]]++;
        return used;
    }
    int isap()
    {
        bfs();
        while(dep[s]<n)
        {
            memcpy(cur,h,sizeof(h));
            maxflow+=dfs(s,0x7fffffffffffffff);
        }
        return maxflow;
    }
    signed main()
    {
        scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
        for(int i=1;i<=m;i++)
        {
            int uu,vv,vval;
            scanf("%lld%lld%lld",&uu,&vv,&vval);
            addedge(uu,vv,vval);addedge(vv,uu,0);
        }
        printf("%lld
    ",isap());
        return 0;
    }
    
    1. PP算法

    抽象的预流推进算法,虽然效率很低但还是过了板子。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <queue>
    #define int long long
    using namespace std;
    const int inf=0x7fffffffffffffff;
    int n,m,s,t,cnt=1,h[1010],inque[1010],height[1010],resflow[1010];
    queue<int> q;
    struct edge{int to,nxt,val;}e[10010];
    void addedge(int u,int v,int val)
    {
        e[++cnt]=(edge){v,h[u],val};
        h[u]=cnt;
    }
    void push(int u,int i)
    {
        int nowflow=min(resflow[u],e[i].val);
        e[i].val-=nowflow;
        e[i^1].val+=nowflow;
        resflow[u]-=nowflow;
        resflow[e[i].to]+=nowflow;
        return;
    }
    bool relabel(int u)
    {
        int minheight=inf;
        for(int i=h[u];i;i=e[i].nxt)
            if(e[i].val)
                minheight=min(minheight,height[e[i].to]);
        if(minheight==inf)return 0;
        height[u]=minheight+1;
        return 1;
    }
    int preflow_push()
    {
        height[s]=n;
        for(int i=h[s];i;i=e[i].nxt)
        {
            int p=e[i].to,val=e[i].val;
            if(val)
            {
                e[i].val-=val;
                e[i^1].val+=val;
                resflow[s]-=val;
                resflow[p]+=val;
                if(!inque[p]&&p!=t&&p!=s&&relabel(p))
                {
                    q.push(p);
                    inque[p]=1;
                }
            }
        }
        while(!q.empty())
        {
            int u=q.front();q.pop();
            inque[u]=0;
            for(int i=h[u];i;i=e[i].nxt)
            {
                int p=e[i].to;
                if(resflow[u]==0)break;
                if(!e[i].val)continue;
                if(height[u]==height[p]+1)push(u,i);
                if(!inque[p]&&resflow[p]!=0&&p!=t&&p!=s&&relabel(p))
                {
                    q.push(p);
                    inque[p]=1;
                }
            }
            if(resflow[u]&&inque[u]==0&&relabel(u))
            {
                q.push(u);
                inque[u]=1;
            }
        }
        return resflow[t];
    }
    signed main()
    {
        scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
        for(int i=1;i<=m;i++)
        {
            int uu,vv,vval;
            scanf("%lld%lld%lld",&uu,&vv,&vval);
            addedge(uu,vv,vval);addedge(vv,uu,0);
        }
        printf("%lld
    ",preflow_push());
        return 0;
    }
    
    1. HLPP算法

    每次推流都推的是高度最高的顶点,从而大大提高了效率。

  • 相关阅读:
    面试题:增强一个对象的方法的三种方式
    Spring笔记01_下载_概述_监听器
    mybatis笔记02
    mybatis笔记01
    muduo学习笔记(二)Reactor关键结构
    Muduo学习笔记(一) 什么都不做的EventLoop
    一个linux下简单的纯C++实现Http请求类(GET,POST,上传,下载)
    一个轻巧高效的多线程c++stream风格异步日志(二)
    一个轻巧高效的多线程c++stream风格异步日志(一)
    C++智能指针,指针容器原理及简单实现(auto_ptr,scoped_ptr,ptr_vector).
  • 原文地址:https://www.cnblogs.com/pjykk/p/14322002.html
Copyright © 2011-2022 走看看