• 最大流五大算法简述


    网络最大流各算法总结

    网络最大流的算法共有两大类5种算法 总体如下表:n为顶点数,m为弧的数目,U代表各条弧的最大容量

    算法名称 复杂度 算法概要
    一般增广路算法 (O(nmU)) 采取标号法每次在容量网络中寻找一条增广路进行增广(或在残留网络中每次任意寻找一条增广路进行增广),直至不存在增广路为止。
    最短增广路算法 (O(nm^2)) 每个阶段:在层次网络中,不断用BFS算法进行增广直至不存在增广路为止。如果汇点不在层次网络中,算法结束。
    连续最短增广路算法(Dinic) (O(n^2m)) 在最短增广路算法的基础上改造:在每个阶段,用一个dfs过程实现多次增广。如果汇点不在层次网络中,则算法结束。
    一般预流推进算法 (O (n^2m)) 维护一个预流,不断地对活跃顶点执行推进(Push)操作或重标号(Relabel)操作来调整这个预流,直到不能操作。
    最高标号预流推进算法 (O( n^2sqrt m)) 每次检查具有最高标号的活跃结点
    • 一般增广路算法(Ford-Fulkerson):

      1) 标号过程 每个顶点的标号有两个量:流入该顶点的流量alpha[u] 指明标号从哪个顶点得到pre[u]

      2)调整过程 在每次得到最后V的标号后 根据 a = alpha[n-1] 来对这条增广路进行优化

    (f(u,v) = f(u,v) + a when<u,v>in P+)

    (f(u,v) = f(u,v)+a when <u,v>in P-)

    $ f(u,v) = f(u,v) when <u,v> otin P$

    通过bfs来进行多次增广路的优化 代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    #define MAXN 1000
    #define INF 1000000//根据题目条件改变
    struct ArcType {
        int c,f;//容量,流量
    };
    ArcType Edge[MAXN][MAXN];
    int n,m;
    int flag[MAXN];//顶点状态:-1未标号 0已标号未检查 1已标号以检查
    int pre[MAXN];//标号的第一个分量:指明从哪个点得到,以便找出可改进量和增广路径
    int alpha[MAXN];//标号的第二个分量:可改进量a
    queue<int > q;
    int v;
    void ford() {
        for(;;) {//标号直至不存在可改进路
            memset(flag,0xff,sizeof(flag));//均初始化为-1
            memset(pre,0xff,sizeof(pre));
            memset(alpha,0xff,sizeof(alpha));
            flag[0] = pre[0] = 0; alpha[0] = INF;//对源点标号
            while(!q.empty()) q.pop();//清空队列
            q.push(0);
            while(!q.empty() && flag[n-1] == -1) {
                v = q.front();q.pop();
                for(int i = 0;i < n;i++) {
                    if(flag[i] == -1) {//正向且未饱和
                        if(Edge[v][i].c < INF && Edge[v][i].f < Edge[v][i].c) {
                            flag[i] = 0;pre[i] = v;
                            alpha[i] = min(alpha[v],Edge[v][i].c - Edge[v][i].f);
                            q.push(i);
                        }
                        else if(Edge[i][v].c < INF && Edge[i][v].f > 0) {//反向且非0
                            flag[i] = 0;pre[i] = -v;
                            alpha[i] = min(Edge[i][v].f,alpha[v]);
                            q.push(i);
                        }
                    }
                }
                flag[v] = 1;
            }
            if(flag[n-1] == -1 || alpha[n-1] == 0) break;//当汇点无需调整,退出循环
            int k1 = n-1,k2 = abs(pre[k1]);
            int a = alpha[n-1];    
            while(1) {//沿着路径改进
                if(Edge[k2][k1].f < INF) Edge[k2][k1].f += a;
                else Edge[k1][k2].f -= a;
                if(k2 == 0) break;
                k1 = k2; k2 = abs(pre[k2]);
            }
        }
        int maxFlow = 0;
        for(int i = 0;i < n;i++) {
            for(int j = 0;j < n;j++) {
                if(i == 0 && Edge[i][j].f < INF) maxFlow+=Edge[i][j].f;//求出源点的流出量
                if(Edge[i][j].f < INF) printf("%d->%d:%d
    ",i,j,Edge[i][j].f);
            }
        }
        printf("maxFlow:%d
    ",maxFlow);
    } 
    int main() {
        int u,v,c,f;
        scanf("%d%d",&n,&m);
        for(int i = 0;i < n;i++) {
            for(int j = 0;j < n;j++) Edge[i][j].c = Edge[i][j].f = INF;
        }
        for(int i = 0;i < m;i++) {
            scanf("%d%d%d%d",&u,&v,&c,&f);
            Edge[u][v].c = c;Edge[u][v].f = f;
        }
        ford();
        return 0;
    }
    

    复杂度简要分析:

    ​ 很明显,如果容量网络中各弧的容量和初始流量均为正整数,则Ford-Fulkerson算法每增广一次,流量至少会增加一个单位,因此Ford-Fulkerson算法肯定能在有限的步骤内使得网络流达到最大。类似的理由可以说明如果弧上的容量为有理数时,也可在有限的步骤内使得网络流达到最大。但是如果弧上的容量可以是无理数,则该算法不一定在有限步内终止。

    ​ 由于割$ { Vs} , V-{ Vs}$ 中前向弧的条数最多为n条,因此最大流流量|F|的上界为nU(U表示网络中各个弧的最大容量)。此外,由于每次增广最多需要对所有弧检查一遍,所以Ford-Fulkerson算法的时间复杂度为$ O(mnU)$ 。

    例题:Poj1149

    • 最短增广路算法:

      最短增广路的思想:

      ​ (1)初始化容量网络和网络流。

      ​ (2)构造残留网络和层次网络,若汇点不在层次网络中,则算法结束。

      ​ (3)在层次网络中不断用BFS增广,直到层次网络中没有增广路为止;每次增广完毕,在层次网络中要去掉因改进流量而导致饱和的弧。

      ​ (4)转步骤(2)

    • 连续最短增广路算法(Dinic算法):

      Dinic算法思路:

      ​ Dinic算法的思想也是分阶段地在层次网络中增广。它与最短路算法不同之处是:最短路增广每个阶段执行完一次BFS增广后,要重新启动BFS从源点Vs开始寻找另一条增广路;而在Dinic算法中,只需一次dfs过程就可以实现多次增广,这是Dinic算法的巧妙之处。Dinic算法的具体步骤如下:

      ​ (1)初始化容量网络和网络流

      ​ (2)构造残留网络和层次网络,若汇点不在层次网络中,则算法结束。

      ​ (3)在层次网络中用一次dfs过程进行增广,dfs过程执行完毕,则该阶段的增广也执行完毕。

      ​ (4)转步骤(2)

      在Dinic算法中,只有第(3)步和最短增广路算法不同。效率得到非常大的提高。

      模板如下:

      const int maxn=500+10;
      struct Edge {
          int from,to,cap,flow;
          Edge(){}
          Edge(int f,int t,int c,int fl):from(f),to(t),cap(c),flow(fl){}
      };
      struct Dinic {
          int n,m,s,t;
          vector<Edge> edges;
          vector<int> G[maxn];
          int d[maxn];
          int cur[maxn];
          bool vis[maxn];
       
          void init(int n,int s,int t) {
              this->n=n, this->s=s, this->t=t;
              edges.clear();
              for(int i=0;i<n;i++) G[i].clear();
          }
          void AddEdge(int from,int to,int cap) {
              edges.push_back( Edge(from,to,cap,0) );
              edges.push_back( Edge(to,from,0,0) );
              m=edges.size();
              G[from].push_back(m-2);
              G[to].push_back(m-1);
          }
          bool BFS() {
              queue<int> Q;
              memset(vis,0,sizeof(vis));
              vis[s]=true;
              d[s]=0;
              Q.push(s);
              while(!Q.empty()) {
                  int x=Q.front(); Q.pop();
                  for(int i=0;i<G[x].size();i++) {
                      Edge e=edges[G[x][i]];
                      if(!vis[e.to] && e.cap>e.flow) {
                          vis[e.to]=true;
                          d[e.to] = d[x]+1;
                          Q.push(e.to);
                      }
                  }
              }
              return vis[t];
          }
       
          int DFS(int x,int a) {
              if(x==t || a==0) return a;
              int flow=0,f;
              for(int& i=cur[x];i<G[x].size();++i) {
                  Edge& e=edges[G[x][i]];
                  if(d[e.to]==d[x]+1 && (f=DFS(e.to, min(a,e.cap-e.flow) ) )>0 ) {
                      e.flow+=f;
                      edges[G[x][i]^1].flow-=f;
                      flow+=f;
                      a-=f;
                      if(a==0) break;
                  }
              }
              return flow;
          }
       
          int Max_Flow() {
              int flow=0;
              while(BFS()) {
                  memset(cur,0,sizeof(cur));
                  flow += DFS(s,INF);
              }
              return flow;
          }
      }DC;
       
      
    • 一般预流推进算法

      1.增广路算法的缺点:

      ​ 增广路算法是找到增广路后,立即沿增广路对网络流进行增广。每一次增广可能需要对最多n-1条弧进行操作,因此,每次增广的复杂度为(O(n)) ,在有些情况下,这个操作的代价是很高的。

      2.距离标号

      ​ 对于一个残留网络G',如何确定其精确的距离标号呢?可以从汇点Vt开始,对弧进行广度优先搜索,这一过程的复杂度为(O(m))

    预流:设(f = { f(u,v)}) 是容量网络的一个网络流,如果G的每一条边弧都满足: (0leq f(u,v) leq c(u,v))

    另外,除源点汇点以外每个顶点u的盈余e(u)都满足:(e(u) geq 0) 则称该网络流是G的预流。

    ​ 预流推进通过不断的对活跃节点的改进来达到最大流

    模板如下:

    using namespace std;
    const int maxn = 110;
    const int maxf = 0x7fffffff;
    int n,np,nc,m;
    int resi[maxn][maxn];
    deque<int> act;
    int h[maxn];
    int ef[maxn];
    int s,t,V;
    void push_relabel() {
        int sum = 0;
        int u,v,p;
        for(int i = 1;i <= V;i++) h[i] = 0;
        h[s] = V;
        memset(ef,0,sizeof(ef));
        ef[s] = maxf;ef[t] = -maxf;
        act.push_front(s);
        while(!act.empty()) {
            u = act.back();
            act.pop_back();
            for(int i = 1;i <= V;i++) {
                v = i;
                if(resi[u][v] < ef[u]) p = resi[u][v];
                else p = ef[u];
                if(p > 0 && (u == s || h[u] == h[v] + 1)) {
                    resi[u][v]-=p;resi[v][u]+=p;
                    if(v == t) sum+=p;
                    ef[u]-=p;ef[v]+=p;
                    if(v != s && v != t) act.push_front(v);
                }
            }
            if(u != s && u != t && ef[u] > 0) {
                h[u]++;
                act.push_front(u);
            }
        } 
        printf("%d
    ",sum);
    }
    
    我现在最大的问题就是人蠢而且还懒的一批。
  • 相关阅读:
    柱状图
    开源版本Visifire的应用
    Jmeter录制脚本-BadBoy
    Jemter环境搭建
    DOS命令
    OSI七层模型
    Google浏览器PostMan插件版安装步骤
    理解JavaScript的caller,callee,call,apply
    .net http请求
    使用命令对象代替switch语句的写法示例
  • 原文地址:https://www.cnblogs.com/pot-a-to/p/10949209.html
Copyright © 2020-2023  润新知