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

    网络流(网络最大流)学习笔记2

    上次写了那个网络流笔记1,是关于FF算法的。

    这次来一发拓展——EK算法Dinic算法

    I-sap 先咕咕咕算了

    Emdond-Karp 算法

    Edmond-Karp算法(下简称EK算法)是对FF算法的bfs优化。

    原先dfs算起来有可能搜到好几次,但是广搜对于判重有着天然的优势。

    所以bfs优化以后时间复杂度((O(VE^2)))反而小了。

    需要用到的是残量网络,即可行流流过去剩下的可以流的大小。

    先放一个邻接矩阵的(Luogu【模板】网络最大流 上面MLE了三个点 70pts)

    #include <iostream>
    #include <cstdio>
    #include <queue>
    
    using namespace std;
    const int INF=2100000000,maxn=10001;
    int left_flow[maxn][maxn];
    int pre[maxn];
    int flow[maxn];
    int n,m;
    
    inline int bfs(int S,int T)
    {
        queue<int>q;
        for (register int i=1; i<=n; i++)
        {
            pre[i]=-1;
        }
        pre[S]=0;
        flow[S]=INF;
        q.push(S);
        while (!q.empty())
        {
            int from=q.front();
            q.pop();
            if (from==T)
                break;
            for (register int i=1; i<=n; i++)
            {
                if (i!=S && left_flow[from][i]>0 && pre[i]==-1)
                {
                    pre[i]=from;
                    flow[i]=min(flow[from],left_flow[from][i]);
                    q.push(i);
                }
            }
        }
        if (pre[T]==-1)
            return -1;
        else
            return flow[T];
    }
    
    inline int EK(int S,int T)
    {
        int canflow=0,nowflow=0;
        while (canflow=bfs(S,T),canflow!=-1)
        {
            int now=T;
            while(now!=S)
            {
                int from=pre[now];
                left_flow[from][now]-=canflow;
                left_flow[now][from]+=canflow;
                now=from;
            }
            nowflow+=canflow;
        }
        return nowflow;
    }
    
    int main()
    {
        int S,T;
        scanf("%d%d%d%d",&n,&m,&S,&T);
        for (register int i=1; i<=m; i++)
        {
            int f,t,v;
            scanf("%d%d%d",&f,&t,&v);
            if (f==t)
                continue;
            left_flow[f][t]+=v;
        }
        printf("%d
    ",EK(S,T));
        return 0;
    }
    

    因为邻接矩阵实在是效率太低啦

    所以我们换成链式前向星,保证只遍历出边。

    #include <iostream>
    #include <cstdio>
    #include <queue>
    
    using namespace std;
    const int INF=2100000000,maxn=10001;
    struct edge{
        int from,to,rev,next,left_flow;
    };
    edge Edge[200001];
    int cnt=1,head[10001];
    inline void add_edge(int from,int to,int flow)
    {
        //正向边 
              Edge[cnt].from=from,
                  Edge[cnt].to=to,
         Edge[cnt].left_flow=flow,
        Edge[cnt].next=head[from],
              Edge[cnt].rev=cnt+1;
        head[from]=cnt++;
        //反边 
                Edge[cnt].from=to,
                Edge[cnt].to=from,
            Edge[cnt].left_flow=0,
          Edge[cnt].next=head[to],
              Edge[cnt].rev=cnt-1;
        head[to]=cnt++;
    }
    int pre[maxn],flow[maxn],n,m;
    
    inline int bfs(int S,int T)
    {
        queue<int> q;
        for (register int i=1;i<=n;i++)
            pre[i]=-1;
        flow[S]=INF;
        pre[S]=0;
        q.push(S);
        while (!q.empty())
        {
            int from=q.front();
            q.pop();
            if (from==T)break;
            for (register int i=head[from];i;i=Edge[i].next)
            {
                int to=Edge[i].to;
                if (Edge[i].left_flow>0 && pre[to]==-1)
                {
                    pre[to]=i;
                    flow[to]=min(flow[from],Edge[i].left_flow);
                    q.push(to);
                }
            }
        }
        if (pre[T]==-1)
            return -1;
        else return flow[T];
    }
    
    inline int EK(int S,int T)
    {
        int canflow=0,maxflow=0;
        while (canflow=bfs(S,T),canflow!=-1)
        {
            int now=T;
            while (now!=S)
            {
                Edge[pre[now]].left_flow-=canflow;
                Edge[Edge[pre[now]].rev].left_flow+=canflow;
                now=Edge[pre[now]].from;
            }
            maxflow+=canflow;
        }
        return maxflow;
    }
    
    int main()
    {
        int S,T;
        scanf("%d%d%d%d",&n,&m,&S,&T);
        for (register int i=1;i<=m;i++)
        {
            int f,t,v;
            scanf("%d%d%d",&f,&t,&v);
            if (f==t)continue;
            add_edge(f,t,v);
        }
        printf("%d
    ",EK(S,T));
        return 0;
    }
    

    (话说我为了27行眼瞎直接调了一晚上

    Dinic 算法

    Magic!将dfs和bfs相结合,分层之后时间复杂度下来了。

    好像在一般情况下,变成了(O(V^2E)),而一般(Vleq E),所以Dinic优化的不是一点半点。

    (下一个系列再说吧QAQ

  • 相关阅读:
    C#开发代码的小技巧1
    17. 装箱、拆箱的最小化
    Google Map开发(一) ASP.NET中调用Google Map API实现简单的地图显示
    C#可空类型
    C#如何扩展类型的内置方法
    Linq合并两个DataTable
    <br style="clear:both" /><br />
    GridView内容<br />换行
    数据库连接字符串大全
    JS将Table导出到Excel
  • 原文地址:https://www.cnblogs.com/jelly123/p/11028784.html
Copyright © 2011-2022 走看看